colinhacks/zod · error · Error

Duplicate schema id "${id}" detected during JSON Schema conv

Error message

Duplicate schema id "${id}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.

What it means

Thrown in `extractDefs` during JSON-Schema conversion. Zod scans every schema it has seen and collects their `.meta({ id })` values; if two distinct schema objects share the same `id`, conversion aborts because the `$defs`/`definitions` keys would collide and `$ref`s would be ambiguous. Ids must be unique across all schemas converted together.

Source

Thrown at packages/zod/src/v4/core/to-json-schema.ts:234

export function extractDefs<T extends schemas.$ZodType>(
  ctx: ToJSONSchemaContext,
  schema: T
  // params: EmitParams
): void {
  // iterate over seen map;
  const root = ctx.seen.get(schema);

  if (!root) throw new Error("Unprocessed schema. This is a bug in Zod.");

  // Track ids to detect duplicates across different schemas
  const idToSchema = new Map<string, schemas.$ZodType>();
  for (const entry of ctx.seen.entries()) {
    const id = ctx.metadataRegistry.get(entry[0])?.id;
    if (id) {
      const existing = idToSchema.get(id);
      if (existing && existing !== entry[0]) {
        throw new Error(
          `Duplicate schema id "${id}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`
        );
      }
      idToSchema.set(id, entry[0]);
    }
  }

  // returns a ref to the schema
  // defId will be empty if the ref points to an external schema (or #)
  const makeURI = (entry: [schemas.$ZodType<unknown, unknown>, Seen]): { ref: string; defId?: string } => {
    // comparing the seen objects because sometimes
    // multiple schemas map to the same seen object.
    // e.g. lazy

    // external is configured
    const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
    if (ctx.external) {
      const externalId = ctx.external.registry.get(entry[0])?.id; // ?? "__shared";// `__schema${ctx.counter++}`;

View on GitHub (pinned to 912f0f51b0)

Solutions

  1. Give each schema a globally unique `id` in its `.meta({ id })` call.
  2. Search the codebase for the duplicate id reported in the message and rename one of them.
  3. If two schemas are genuinely meant to be the same, share a single schema object/reference rather than defining two with the same id.

Example fix

// before
const A = z.object({ name: z.string() }).meta({ id: 'User' });
const B = z.object({ email: z.string() }).meta({ id: 'User' });
z.toJSONSchema(z.object({ a: A, b: B })); // throws

// after
const A = z.object({ name: z.string() }).meta({ id: 'NamedUser' });
const B = z.object({ email: z.string() }).meta({ id: 'EmailUser' });
z.toJSONSchema(z.object({ a: A, b: B }));
Defensive patterns

Strategy: validation

Validate before calling

function assertUniqueIds(schemas) {
  const seen = new Map();
  for (const s of schemas) {
    const id = s._zod?.def?.meta?.id ?? (s as any).meta?.id;
    // id is usually registered via .meta({ id }); check via the registry if available
    if (id && seen.has(id) && seen.get(id) !== s) throw new Error(`duplicate id "${id}"`);
    if (id) seen.set(id, s);
  }
}

Try / catch

try {
  z.toJSONSchema(root);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Duplicate schema id')) {
    // grep the codebase for the reported id and rename one occurrence
  }
  throw e;
}

Prevention

When it happens

Trigger: Registering two different schemas with the same id, e.g. `A.meta({ id: 'User' })` and `B.meta({ id: 'User' })` where A and B are different objects, then converting a schema that references both (e.g. via intersection, union, or a shared registry).

Common situations: Reusing an id constant across modules; copying a schema definition and forgetting to change its id; merging schemas from multiple files that each tag their primary type with the same generic name like `'Item'`.

Related errors


AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03). Data as JSON: /data/errors/da72081f692c3ed2.json. Report an issue: GitHub.