{"id":"7a266e4ca8109c66","repo":"colinhacks/zod","slug":"dynamic-catch-values-are-not-supported-in-json-sch","errorCode":null,"errorMessage":"Dynamic catch values are not supported in JSON Schema","messagePattern":"Dynamic catch values are not supported in JSON Schema","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v4/core/json-schema-processors.ts","lineNumber":522,"sourceCode":"export const prefaultProcessor: Processor<schemas.$ZodPrefault> = (schema, ctx, json, params) => {\n  const def = schema._zod.def as schemas.$ZodPrefaultDef;\n  process(def.innerType, ctx as any, params);\n  const seen = ctx.seen.get(schema)!;\n  seen.ref = def.innerType;\n  if (ctx.io === \"input\") json._prefault = JSON.parse(JSON.stringify(def.defaultValue));\n};\n\nexport const catchProcessor: Processor<schemas.$ZodCatch> = (schema, ctx, json, params) => {\n  const def = schema._zod.def as schemas.$ZodCatchDef;\n  process(def.innerType, ctx as any, params);\n  const seen = ctx.seen.get(schema)!;\n  seen.ref = def.innerType;\n  let catchValue: any;\n  try {\n    catchValue = def.catchValue(undefined as any);\n  } catch {\n    if (ctx.unrepresentable === \"throw\") {\n      throw new Error(\"Dynamic catch values are not supported in JSON Schema\");\n    }\n    return;\n  }\n  json.default = catchValue;\n};\n\nexport const pipeProcessor: Processor<schemas.$ZodPipe> = (schema, ctx, _json, params) => {\n  const def = schema._zod.def as schemas.$ZodPipeDef;\n  const inIsTransform = def.in._zod.traits.has(\"$ZodTransform\");\n  const innerType = ctx.io === \"input\" ? (inIsTransform ? def.out : def.in) : def.out;\n  process(innerType, ctx as any, params);\n  const seen = ctx.seen.get(schema)!;\n  seen.ref = innerType;\n};\n\nexport const readonlyProcessor: Processor<schemas.$ZodReadonly> = (schema, ctx, json, params) => {\n  const def = schema._zod.def as schemas.$ZodReadonlyDef;\n  process(def.innerType, ctx as any, params);","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/json-schema-processors.ts#L504-L540","documentation":"`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'.","triggerScenarios":"`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.","commonSituations":"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.","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)."],"exampleFix":"// before\nconst s = z.string().catch(() => getConfig().fallback);\nz.toJSONSchema(s); // throws\n// after\nz.toJSONSchema(s, { unrepresentable: \"any\" });\n// or, for a static contract:\nconst sStatic = z.string().catch(\"default\");\nz.toJSONSchema(sStatic);","handlingStrategy":"fallback","validationCode":"// Use a constant catch value for schemas you convert.\nconst contractSchema = baseSchema.catch(\"default\");\nconst json = z.toJSONSchema(contractSchema);\n// Or skip dynamic-catch nodes: z.toJSONSchema(schema, { unrepresentable: \"any\" });","typeGuard":"function isDynamicCatch(schema: z.ZodType): boolean {\n  if (!schema._zod.traits.has(\"$ZodCatch\")) return false;\n  try {\n    (schema._zod.def as any).catchValue(undefined as any);\n    return false;\n  } catch {\n    return true;\n  }\n}","tryCatchPattern":"try {\n  return z.toJSONSchema(schema);\n} catch (e) {\n  if (e instanceof Error && /Dynamic catch values are not supported/.test(e.message)) {\n    return z.toJSONSchema(schema, { unrepresentable: \"any\" });\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["json-schema","catch","dynamic-default","unrepresentable"],"analyzedSha":"912f0f51b0ced654d0069741e7160834dca742ee","analyzedAt":"2026-08-03T17:41:55.908Z","schemaVersion":2}