colinhacks/zod · error · Error

Transforms cannot be represented in JSON Schema

Error message

Transforms cannot be represented in JSON Schema

What it means

Thrown by toJSONSchema() when a ZodTransform schema (z.transform(...)) is encountered with unrepresentable 'throw' (default). A transform mutates the output type at runtime; the JSON Schema it would produce is the inner schema's, but the transform itself has no declarative representation and the converter surfaces the mismatch.

Solutions

  1. Pass io: 'input' to toJSONSchema() to convert the pre-transform shape (the wire format), which is usually what external consumers need.
  2. Pass { unrepresentable: 'any' } so the transform collapses to {}.
  3. Split the schema: keep a plain data schema for export, and apply the transform in a separate parsing-only schema.

Example fix

// before (throws on output side)
const Schema = z.string().datetime().transform((s) => new Date(s));
z.toJSONSchema(Schema);

// after (export the input shape)
const Schema = z.string().datetime().transform((s) => new Date(s));
z.toJSONSchema(Schema, { io: 'input' });
// { type: 'string', format: 'date-time' }
Defensive patterns

Strategy: try-catch

Validate before calling

// Export the input (wire) shape to avoid the transform representation problem.
const json = z.toJSONSchema(schema, { io: 'input' });
// Or detect transforms and opt into the any fallback.
const opts = schemaContains(schema, (s) => s._zod.def.type === 'transform')
  ? { io: 'input' }
  : {};
const json2 = z.toJSONSchema(schema, opts);

Type guard

function hasTransform(schema) {
  return schema._zod.traits.has('$ZodTransform');
}

Try / catch

try {
  return z.toJSONSchema(schema);
} catch (e) {
  if (e.message === 'Transforms cannot be represented in JSON Schema') {
    // The input (pre-transform) shape is usually the correct external contract
    return z.toJSONSchema(schema, { io: 'input' });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling toJSONSchema() on a schema whose output side is a transform, e.g. z.string().transform((s) => new Date(s)), without selecting the input side. Exporting a piped schema that ends in a transform.

Common situations: Schemas built for internal parsing that also drive API docs — the transform is implementation detail but blocks JSON Schema generation.

Related errors


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

Appendix: source

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

export const successProcessor: Processor<schemas.$ZodSuccess> = (_schema, _ctx, json, _params) => {
  (json as JSONSchema.BooleanSchema).type = "boolean";
};

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

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

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

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

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

// ==================== COMPOSITE TYPE PROCESSORS ====================

export const arrayProcessor: Processor<schemas.$ZodArray> = (schema, ctx, _json, params) => {

View on GitHub (pinned to 2d90846af9)