Skip to content

Cross-Field Validation

Sometimes individual variable checks aren't enough — the relationship between variables matters too. The top-level validate option lets you assert constraints across the whole parsed environment after all per-field checks have passed.

Basic Usage

ts
const env = createEnv(
  {
    HTTP_PORT: { type: "number", required: true },
    HTTPS_PORT: { type: "number", required: true },
  },
  {
    validate: (env) => env.HTTP_PORT !== env.HTTPS_PORT,
  },
);

If the function returns false, env-guard throws a generic cross-field validation error:

❌ Cross-field validation failed.

Custom Error Message

Return a string from validate to provide a human-readable message:

ts
const env = createEnv(
  {
    HTTP_PORT: { type: "number", required: true },
    HTTPS_PORT: { type: "number", required: true },
  },
  {
    validate: (env) =>
      env.HTTP_PORT !== env.HTTPS_PORT ||
      "HTTP_PORT and HTTPS_PORT must be different",
  },
);

Error output:

❌ HTTP_PORT and HTTPS_PORT must be different

Typed Environment

The validate callback receives the fully-typed, parsed environment — the same type that createEnv returns. TypeScript guarantees that every property is the correct type (e.g. number, not string):

ts
const env = createEnv(
  {
    MIN_POOL_SIZE: { type: "number", required: true },
    MAX_POOL_SIZE: { type: "number", required: true },
  },
  {
    // env.MIN_POOL_SIZE and env.MAX_POOL_SIZE are inferred as `number`
    validate: (env) =>
      env.MIN_POOL_SIZE <= env.MAX_POOL_SIZE ||
      `MIN_POOL_SIZE (${env.MIN_POOL_SIZE}) must be ≤ MAX_POOL_SIZE (${env.MAX_POOL_SIZE})`,
  },
);

With Custom Error Handler

When onError is supplied, the cross-field error is passed to it the same way as per-field errors:

ts
const env = createEnv(
  {
    DB_HOST: { type: "string", required: true },
    DB_PORT: { type: "number", required: true },
  },
  {
    validate: (env) => env.DB_PORT > 1024 || "DB_PORT must be above 1024",
    onError: (errors) => {
      console.error("Environment configuration errors:", errors);
      process.exit(1);
    },
  },
);

Behaviour Summary

ScenarioOutcome
Returns truePasses — no error
Returns falseGeneric error: "Cross-field validation failed."
Returns a non-empty stringThat string is used as the error message
Per-field errors are presentvalidate is not called — per-field errors are reported first
Used with watch: trueRe-evaluated on every refresh() call

Released under the MIT License.