colinhacks/zod · error · Error
Map cannot be represented in JSON Schema
Error message
Map cannot be represented in JSON Schema
What it means
Thrown by toJSONSchema() when a ZodMap schema (z.map(keySchema, valueSchema)) is encountered with unrepresentable 'throw' (default). JSON objects only allow string keys and have no first-class map type with per-key validation, so a typed Map cannot be faithfully expressed (especially with non-string keys).
Solutions
- Pass { unrepresentable: 'any' } to toJSONSchema() so the map field becomes {}.
- Model the external contract as z.record(z.string(), valueSchema) (JSON object with string keys) and convert to/from Map at the boundary.
- If keys are enumerable, use z.object({ ... }) or an enum-keyed record instead.
Example fix
// before (throws)
const Schema = z.map(z.string(), z.number());
z.toJSONSchema(Schema);
// after (record for the JSON contract)
const Schema = z.record(z.string(), z.number());
z.toJSONSchema(Schema);
// { type: 'object', additionalProperties: { type: 'number' } } Defensive patterns
Strategy: try-catch
Validate before calling
const opts = schemaContains(schema, (s) => s._zod.def.type === 'map')
? { unrepresentable: 'any' }
: {};
const json = z.toJSONSchema(schema, opts); Type guard
function hasMap(schema) {
return schema._zod.def.type === 'map';
} Try / catch
try {
return z.toJSONSchema(schema);
} catch (e) {
if (e.message === 'Map cannot be represented in JSON Schema') {
return z.toJSONSchema(schema, { unrepresentable: 'any' });
}
throw e;
} Prevention
- Model external contracts with z.record(z.string(), valueSchema) instead of z.map().
- Convert Map <-> plain object at the boundary; keep z.map() for runtime-only schemas.
- Default to { unrepresentable: 'any' } when exporting models that may still use Map.
When it happens
Trigger: Calling toJSONSchema() on z.map(z.string(), z.number()) or any schema embedding a z.map(). OpenAPI generation for an API whose body uses Map.
Common situations: Runtime schemas that exploit Map for arbitrary-key lookups, then needing JSON Schema/OpenAPI output that downstream tooling can consume.
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/33ed11f9b31c922b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/zod/src/v4/core/json-schema-processors.ts:264
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) => {
const json = _json as JSONSchema.ArraySchema;
const def = schema._zod.def as schemas.$ZodArrayDef;
const { minimum, maximum } = schema._zod.bag;
if (typeof minimum === "number") json.minItems = minimum;
if (typeof maximum === "number") json.maxItems = maximum;
View on GitHub (pinned to 2d90846af9)