{"record":{"id":"da72081f692c3ed2","repo":"colinhacks/zod","slug":"duplicate-schema-id-id-detected-during-json-s","errorCode":null,"errorMessage":"Duplicate schema id \"${id}\" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.","messagePattern":"Duplicate schema id \"(.+?)\" detected during JSON Schema conversion\\. Two different schemas cannot share the same id when converted together\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v4/core/to-json-schema.ts","lineNumber":235,"sourceCode":"\nexport function extractDefs<T extends schemas.$ZodType>(\n  ctx: ToJSONSchemaContext,\n  schema: T\n  // params: EmitParams\n): void {\n  // iterate over seen map;\n  const root = ctx.seen.get(schema);\n\n  if (!root) throw new Error(\"Unprocessed schema. This is a bug in Zod.\");\n\n  // Track ids to detect duplicates across different schemas\n  const idToSchema = new Map<string, schemas.$ZodType>();\n  for (const entry of ctx.seen.entries()) {\n    const id = ctx.metadataRegistry.get(entry[0])?.id;\n    if (id) {\n      const existing = idToSchema.get(id);\n      if (existing && existing !== entry[0]) {\n        throw new Error(\n          `Duplicate schema id \"${id}\" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`\n        );\n      }\n      idToSchema.set(id, entry[0]);\n    }\n  }\n\n  // returns a ref to the schema\n  // defId will be empty if the ref points to an external schema (or #)\n  const makeURI = (entry: [schemas.$ZodType<unknown, unknown>, Seen]): { ref: string; defId?: string } => {\n    // comparing the seen objects because sometimes\n    // multiple schemas map to the same seen object.\n    // e.g. lazy\n\n    // external is configured\n    const defsSegment = ctx.target === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n    if (ctx.external) {\n      const externalId = ctx.external.registry.get(entry[0])?.id; // ?? \"__shared\";// `__schema${ctx.counter++}`;","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/colinhacks/zod/blob/2d90846af918af9602e088812d63a035d47cdbe4/packages/zod/src/v4/core/to-json-schema.ts#L217-L253","documentation":"Thrown by extractDefs() while preparing $defs during JSON Schema conversion. The function walks ctx.seen and indexes every schema by its metadata id; if two distinct schema objects share the same id (existing && existing !== entry[0]), the conversion aborts because $defs keys must be unique. The id comes from ctx.metadataRegistry (default globalRegistry), typically set via .meta({ id: \"...\" }) or .register().","triggerScenarios":"Registering two different schemas with the same id: z.schemaA.meta({ id: \"user\" }) and z.schemaB.meta({ id: \"user\" }), then converting a parent schema that references both; using a shared id constant across modules; copy-pasting a schema and its meta() without changing the id; two versions of a schema loaded via different import paths.","commonSituations":"Monorepo modules each registering their own \"User\" schema with id \"user\"; refactoring that splits one schema into two but keeps the id; dynamic schema factories that reuse a hardcoded id; test fixtures clashing with production registrations in the global registry.","solutions":["Give each schema a unique id in .meta({ id: ... }) or .register().","If a duplicate is unintentional, find the second registration via a grep for the id and rename it.","Use a namespaced id convention (\"users.User\", \"orders.User\") to avoid cross-module collisions.","Clear or scope the global registry between unrelated conversions if leakage is the cause."],"exampleFix":"// before\nconst A = z.object({ x: z.string() }).meta({ id: \"thing\" });\nconst B = z.object({ y: z.number() }).meta({ id: \"thing\" }); // duplicate\n// after\nconst A = z.object({ x: z.string() }).meta({ id: \"thingA\" });\nconst B = z.object({ y: z.number() }).meta({ id: \"thingB\" });","handlingStrategy":"validation","validationCode":"function assertUniqueIds(schemas: z.ZodType[], registry = z.globalRegistry) {\n  const seen = new Map<string, z.ZodType>();\n  for (const s of schemas) {\n    const id = registry.get(s)?.id;\n    if (!id) continue;\n    if (seen.has(id) && seen.get(id) !== s) throw new Error(`Duplicate schema id: ${id}`);\n    seen.set(id, s);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use unique, namespaced ids in .meta({ id }) and .register().","Audit schema registrations when splitting modules.","Clear the global registry in long-lived test processes if fixtures collide."],"tags":["json-schema","conversion","id","duplicate","metadata"],"backgroundTag":null,"analyzedSha":"2d90846af918af9602e088812d63a035d47cdbe4","analyzedAt":"2026-08-11T01:21:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}