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
- Pass `{ unrepresentable: "any" }` so the dynamic-catch node is skipped (no `default` emitted).
- Use a constant catch value (`.catch(defaultValue)`) for the schema you convert to JSON Schema; keep dynamic catch for the runtime-only schema.
- 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
- Use constant catch values (`.catch(value)`) for schemas destined for JSON Schema.
- Keep dynamic-catch schemas for runtime parsing only; maintain a static variant for contracts.
- Apply `{ unrepresentable: "any" }` when converting schemas with function-form catch.
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
- BigInt cannot be represented in JSON Schema
- Symbols cannot be represented in JSON Schema
- Undefined cannot be represented in JSON Schema
- Void cannot be represented in JSON Schema
- Date cannot be represented in JSON Schema
AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03).
Data as JSON: /data/errors/7a266e4ca8109c66.json.
Report an issue: GitHub.