colinhacks/zod · error · Error
NaN cannot be represented in JSON Schema
Error message
NaN cannot be represented in JSON Schema
What it means
Thrown by toJSONSchema() when a ZodNaN schema (z.nan()) is encountered with unrepresentable 'throw' (default). NaN is not a valid JSON number (JSON.parse would never produce it), so JSON Schema cannot represent it.
Solutions
- Pass { unrepresentable: 'any' } to toJSONSchema() so the NaN branch becomes {}.
- Remove z.nan() from the exported schema; model NaN handling at runtime only.
- If the external contract needs a sentinel, use an explicit string literal ('NaN') and translate at the boundary.
Example fix
// before (throws)
const Schema = z.union([z.number(), z.nan()]);
z.toJSONSchema(Schema);
// after
const Schema = z.union([z.number(), z.nan()]);
z.toJSONSchema(Schema, { unrepresentable: 'any' });
// NaN branch collapses to {}; union becomes anyOf [ {type:'number'}, {} ] Defensive patterns
Strategy: try-catch
Validate before calling
const opts = schemaContains(schema, (s) => s._zod.def.type === 'nan')
? { unrepresentable: 'any' }
: {};
const json = z.toJSONSchema(schema, opts); Type guard
function hasNaN(schema) {
return schema._zod.def.type === 'nan';
} Try / catch
try {
return z.toJSONSchema(schema);
} catch (e) {
if (e.message === 'NaN cannot be represented in JSON Schema') {
return z.toJSONSchema(schema, { unrepresentable: 'any' });
}
throw e;
} Prevention
- Do not include z.nan() in exported schemas; handle NaN purely at runtime.
- Use a string sentinel ('NaN') if the external contract needs one.
- Pass { unrepresentable: 'any' } when exporting schemas with numeric unions that include NaN.
When it happens
Trigger: Calling toJSONSchema() on a schema that includes z.nan(), e.g. as a sentinel in a union. Exporting a model that intentionally admits NaN.
Common situations: Numerical computation schemas that flag NaN results, then exporting the schema for documentation or external validation.
Related errors
- BigInt cannot be represented in JSON Schema
- BigInt literals cannot be represented in JSON Schema
- Custom types cannot be represented in JSON Schema
- Date cannot be represented in JSON Schema
- Dynamic catch values are not supported in JSON Schema
AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11).
Data as JSON: /api/errors/25a6f2edda964aaa.
Report an issue: GitHub.
Appendix: source
Thrown at packages/zod/src/v4/core/json-schema-processors.ts:204
const val = vals[0]!;
json.type = val === null ? ("null" as const) : (typeof val as any);
if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
json.enum = [val];
} else {
json.const = val;
}
} else {
if (vals.every((v) => typeof v === "number")) json.type = "number";
if (vals.every((v) => typeof v === "string")) json.type = "string";
if (vals.every((v) => typeof v === "boolean")) json.type = "boolean";
if (vals.every((v) => v === null)) json.type = "null";
json.enum = vals;
}
};
export const nanProcessor: Processor<schemas.$ZodNaN> = (_schema, ctx, _json, _params) => {
if (ctx.unrepresentable === "throw") {
throw new Error("NaN cannot be represented in JSON Schema");
}
};
export const templateLiteralProcessor: Processor<schemas.$ZodTemplateLiteral> = (schema, _ctx, json, _params) => {
const _json = json as JSONSchema.StringSchema;
const pattern = schema._zod.pattern;
if (!pattern) throw new Error("Pattern not found in template literal");
_json.type = "string";
_json.pattern = pattern.source;
};
export const fileProcessor: Processor<schemas.$ZodFile> = (schema, _ctx, json, _params) => {
const _json = json as JSONSchema.StringSchema;
const file: JSONSchema.StringSchema = {
type: "string",
format: "binary",
contentEncoding: "binary",
};View on GitHub (pinned to 2d90846af9)