colinhacks/zod · error · Error

Date cannot be represented in JSON Schema

Error message

Date cannot be represented in JSON Schema

What it means

Thrown by toJSONSchema() when a ZodDate schema (z.date()) is encountered with unrepresentable 'throw' (default). ZodDate validates a JS Date object instance; JSON Schema has no date-instance type (only string formats like 'date-time'), so emitting one would change the validation semantics and the converter refuses.

Solutions

  1. For the exported/JSON contract, use z.iso.datetime() or z.string().datetime() (which produce { type: 'string', format: 'date-time' }) instead of z.date().
  2. Pass { unrepresentable: 'any' } to toJSONSchema() to emit {} for date fields if you accept the loss of validation metadata.
  3. Maintain two schemas: a runtime schema with z.date() and an export schema with z.iso.datetime(), sharing field shape via composition.

Example fix

// before (throws)
const Schema = z.object({ createdAt: z.date() });
z.toJSONSchema(Schema);

// after (string date-time for the JSON contract)
const Schema = z.object({ createdAt: z.iso.datetime() });
z.toJSONSchema(Schema);
// { type: 'object', properties: { createdAt: { type: 'string', format: 'date-time' } } }
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer exporting the wire (string) shape rather than the Date-instance shape.
function exportSchema(runtimeSchema) {
  // Replace z.date() with z.iso.datetime() for export, or accept the any fallback.
  try {
    return z.toJSONSchema(runtimeSchema);
  } catch (e) {
    if (e.message === 'Date cannot be represented in JSON Schema') {
      return z.toJSONSchema(runtimeSchema, { unrepresentable: 'any' });
    }
    throw e;
  }
}

Type guard

function hasDate(schema) {
  return schema._zod.def.type === 'date';
}

Try / catch

try {
  return z.toJSONSchema(schema);
} catch (e) {
  if (e.message === 'Date cannot be represented in JSON Schema') {
    // Either downgrade to string date-time or allow the any fallback
    return z.toJSONSchema(schema, { unrepresentable: 'any' });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling toJSONSchema() on a schema containing z.date(). Generating OpenAPI from a model whose timestamps are Date instances rather than ISO strings.

Common situations: Server models that parse into Date objects for runtime use, but whose wire/API contract is an ISO 8601 string — exporting the runtime schema directly mismatches the wire format.

Related errors


AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11). Data as JSON: /api/errors/168b5634da54477b. Report an issue: GitHub.

Appendix: source

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

    throw new Error("Void cannot be represented in JSON Schema");
  }
};

export const neverProcessor: Processor<schemas.$ZodNever> = (_schema, _ctx, json, _params) => {
  json.not = {};
};

export const anyProcessor: Processor<schemas.$ZodAny> = (_schema, _ctx, _json, _params) => {
  // empty schema accepts anything
};

export const unknownProcessor: Processor<schemas.$ZodUnknown> = (_schema, _ctx, _json, _params) => {
  // empty schema accepts anything
};

export const dateProcessor: Processor<schemas.$ZodDate> = (_schema, ctx, _json, _params) => {
  if (ctx.unrepresentable === "throw") {
    throw new Error("Date cannot be represented in JSON Schema");
  }
};

export const enumProcessor: Processor<schemas.$ZodEnum> = (schema, _ctx, json, _params) => {
  const def = schema._zod.def as schemas.$ZodEnumDef;
  const values = getEnumValues(def.entries);
  // Number enums can have both string and number values
  if (values.every((v) => typeof v === "number")) json.type = "number";
  if (values.every((v) => typeof v === "string")) json.type = "string";
  json.enum = values;
};

export const literalProcessor: Processor<schemas.$ZodLiteral> = (schema, ctx, json, _params) => {
  const def = schema._zod.def as schemas.$ZodLiteralDef<any>;
  const vals: (string | number | boolean | null)[] = [];
  for (const val of def.values) {
    if (val === undefined) {
      if (ctx.unrepresentable === "throw") {

View on GitHub (pinned to 2d90846af9)