{"record":{"id":"f7f46596d5985a28","repo":"colinhacks/zod","slug":"custom-types-cannot-be-represented-in-json-schema","errorCode":null,"errorMessage":"Custom types cannot be represented in JSON Schema","messagePattern":"Custom types cannot be represented in JSON Schema","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v4/core/json-schema-processors.ts","lineNumber":246,"sourceCode":"    if (mime.length === 1) {\n      file.contentMediaType = mime[0]!;\n      Object.assign(_json, file);\n    } else {\n      Object.assign(_json, file); // shared props at root\n      _json.anyOf = mime.map((m) => ({ contentMediaType: m })); // only contentMediaType differs\n    }\n  } else {\n    Object.assign(_json, file);\n  }\n};\n\nexport const successProcessor: Processor<schemas.$ZodSuccess> = (_schema, _ctx, json, _params) => {\n  (json as JSONSchema.BooleanSchema).type = \"boolean\";\n};\n\nexport const customProcessor: Processor<schemas.$ZodCustom> = (_schema, ctx, _json, _params) => {\n  if (ctx.unrepresentable === \"throw\") {\n    throw new Error(\"Custom types cannot be represented in JSON Schema\");\n  }\n};\n\nexport const functionProcessor: Processor<schemas.$ZodFunction> = (_schema, ctx, _json, _params) => {\n  if (ctx.unrepresentable === \"throw\") {\n    throw new Error(\"Function types cannot be represented in JSON Schema\");\n  }\n};\n\nexport const transformProcessor: Processor<schemas.$ZodTransform> = (_schema, ctx, _json, _params) => {\n  if (ctx.unrepresentable === \"throw\") {\n    throw new Error(\"Transforms cannot be represented in JSON Schema\");\n  }\n};\n\nexport const mapProcessor: Processor<schemas.$ZodMap> = (_schema, ctx, _json, _params) => {\n  if (ctx.unrepresentable === \"throw\") {\n    throw new Error(\"Map cannot be represented in JSON Schema\");","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/colinhacks/zod/blob/2d90846af918af9602e088812d63a035d47cdbe4/packages/zod/src/v4/core/json-schema-processors.ts#L228-L264","documentation":"Thrown by toJSONSchema() when a ZodCustom schema (z.custom(...)) is encountered with unrepresentable 'throw' (default). A custom check carries arbitrary validation logic with no declarative JSON Schema equivalent, so the converter cannot faithfully emit a constraint.","triggerScenarios":"Calling toJSONSchema() on a schema containing z.custom(() => check(...)). Composing a custom validator into a model and then exporting the model to JSON Schema/OpenAPI.","commonSituations":"Refining schemas with business rules (z.custom / .refine) and expecting the JSON Schema output to encode those rules.","solutions":["Pass { unrepresentable: 'any' } to toJSONSchema() so the custom field becomes {}.","Express what you can declaratively (z.string().email(), patterns, min/max) and keep only truly-unrepresentable logic in z.custom — accepting that JSON Schema loses it.","Use the override() hook of toJSONSchema() to inject a custom JSON Schema fragment for the field, decoupling runtime validation from the exported contract."],"exampleFix":"// before (throws)\nconst Even = z.custom((x) => typeof x === 'number' && x % 2 === 0);\nz.toJSONSchema(Even);\n\n// after\nconst Even = z.custom((x) => typeof x === 'number' && x % 2 === 0);\nz.toJSONSchema(Even, { unrepresentable: 'any' }); // {}\n// or declare the representable part and refine separately\nconst Even = z.number().refine((x) => x % 2 === 0);","handlingStrategy":"try-catch","validationCode":"const opts = schemaContains(schema, (s) => s._zod.def.type === 'custom')\n  ? { unrepresentable: 'any' }\n  : {};\nconst json = z.toJSONSchema(schema, opts);","typeGuard":"function hasCustom(schema) {\n  return schema._zod.def.type === 'custom';\n}","tryCatchPattern":"try {\n  return z.toJSONSchema(schema);\n} catch (e) {\n  if (e.message === 'Custom types cannot be represented in JSON Schema') {\n    return z.toJSONSchema(schema, { unrepresentable: 'any' });\n  }\n  throw e;\n}","preventionTips":["Prefer declarative built-in constraints over z.custom() wherever possible — they survive JSON Schema export.","Isolate z.custom()/refine() logic in runtime-only schemas; export a plain data schema.","Use the override() hook to inject a hand-written JSON Schema fragment for fields that need custom validation."],"tags":["json-schema","custom","refine","unrepresentable","to-json-schema"],"backgroundTag":null,"analyzedSha":"2d90846af918af9602e088812d63a035d47cdbe4","analyzedAt":"2026-08-11T01:21:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}