colinhacks/zod · error · Error

Dynamic catch values are not supported in JSON Schema

Error message

Dynamic catch values are not supported in JSON Schema

What it means

`catchProcessor` (json-schema-processors.ts:512) needs a static default value for the produced JSON Schema, so it calls `def.catchValue(undefined)` (schemas.ts:3880) at conversion time. If the catch value is a function that throws when invoked without real input context, the `catch` at line 520 swallows it, and when `ctx.unrepresentable === "throw"` (default) the processor throws 'Dynamic catch values are not supported'.

Source

Thrown at packages/zod/src/v4/core/json-schema-processors.ts:522

export const prefaultProcessor: Processor<schemas.$ZodPrefault> = (schema, ctx, json, params) => {
  const def = schema._zod.def as schemas.$ZodPrefaultDef;
  process(def.innerType, ctx as any, params);
  const seen = ctx.seen.get(schema)!;
  seen.ref = def.innerType;
  if (ctx.io === "input") json._prefault = JSON.parse(JSON.stringify(def.defaultValue));
};

export const catchProcessor: Processor<schemas.$ZodCatch> = (schema, ctx, json, params) => {
  const def = schema._zod.def as schemas.$ZodCatchDef;
  process(def.innerType, ctx as any, params);
  const seen = ctx.seen.get(schema)!;
  seen.ref = def.innerType;
  let catchValue: any;
  try {
    catchValue = def.catchValue(undefined as any);
  } catch {
    if (ctx.unrepresentable === "throw") {
      throw new Error("Dynamic catch values are not supported in JSON Schema");
    }
    return;
  }
  json.default = catchValue;
};

export const pipeProcessor: Processor<schemas.$ZodPipe> = (schema, ctx, _json, params) => {
  const def = schema._zod.def as schemas.$ZodPipeDef;
  const inIsTransform = def.in._zod.traits.has("$ZodTransform");
  const innerType = ctx.io === "input" ? (inIsTransform ? def.out : def.in) : def.out;
  process(innerType, ctx as any, params);
  const seen = ctx.seen.get(schema)!;
  seen.ref = innerType;
};

export const readonlyProcessor: Processor<schemas.$ZodReadonly> = (schema, ctx, json, params) => {
  const def = schema._zod.def as schemas.$ZodReadonlyDef;
  process(def.innerType, ctx as any, params);

View on GitHub (pinned to 912f0f51b0)

Solutions

  1. Pass `{ unrepresentable: "any" }` so the dynamic-catch node is skipped (no `default` emitted).
  2. Use a constant catch value (`.catch(defaultValue)`) for the schema you convert to JSON Schema; keep dynamic catch for the runtime-only schema.
  3. Maintain two variants of the schema: one declarative (for contracts) and one with dynamic catch (for parsing).

Example fix

// before
const s = z.string().catch(() => getConfig().fallback);
z.toJSONSchema(s); // throws
// after
z.toJSONSchema(s, { unrepresentable: "any" });
// or, for a static contract:
const sStatic = z.string().catch("default");
z.toJSONSchema(sStatic);
Defensive patterns

Strategy: fallback

Validate before calling

// Use a constant catch value for schemas you convert.
const contractSchema = baseSchema.catch("default");
const json = z.toJSONSchema(contractSchema);
// Or skip dynamic-catch nodes: z.toJSONSchema(schema, { unrepresentable: "any" });

Type guard

function isDynamicCatch(schema: z.ZodType): boolean {
  if (!schema._zod.traits.has("$ZodCatch")) return false;
  try {
    (schema._zod.def as any).catchValue(undefined as any);
    return false;
  } catch {
    return true;
  }
}

Try / catch

try {
  return z.toJSONSchema(schema);
} catch (e) {
  if (e instanceof Error && /Dynamic catch values are not supported/.test(e.message)) {
    return z.toJSONSchema(schema, { unrepresentable: "any" });
  }
  throw e;
}

Prevention

When it happens

Trigger: `z.toJSONSchema()` over a schema containing `.catch(() => computeDefault())` (a function-form catch whose body depends on real parse context / throws when called with `undefined`), with default options. A constant `.catch(fixedValue)` does NOT trigger this because calling the wrapped `() => fixedValue` succeeds.

Common situations: Catch values that read from external state (DB, config, `Date.now()`) or that validate their argument shape; generating contracts for schemas that use dynamic fallbacks.

Related errors


AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03). Data as JSON: /data/errors/7a266e4ca8109c66.json. Report an issue: GitHub.