{"id":"e5daad3e436f0113","repo":"colinhacks/zod","slug":"schema-is-missing-an-id-property","errorCode":null,"errorMessage":"Schema is missing an `id` property","messagePattern":"Schema is missing an `id` property","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v4/core/to-json-schema.ts","lineNumber":468,"sourceCode":"    flattenRef(entry[0]);\n  }\n\n  const result: JSONSchema.BaseSchema = {};\n  if (ctx.target === \"draft-2020-12\") {\n    result.$schema = \"https://json-schema.org/draft/2020-12/schema\";\n  } else if (ctx.target === \"draft-07\") {\n    result.$schema = \"http://json-schema.org/draft-07/schema#\";\n  } else if (ctx.target === \"draft-04\") {\n    result.$schema = \"http://json-schema.org/draft-04/schema#\";\n  } else if (ctx.target === \"openapi-3.0\") {\n    // OpenAPI 3.0 schema objects should not include a $schema property\n  } else {\n    // Arbitrary string values are allowed but won't have a $schema property set\n  }\n\n  if (ctx.external?.uri) {\n    const id = ctx.external.registry.get(schema)?.id;\n    if (!id) throw new Error(\"Schema is missing an `id` property\");\n    result.$id = ctx.external.uri(id);\n  }\n\n  Object.assign(result, root.def ?? root.schema);\n\n  // The `id` in `.meta()` is a Zod-specific registration tag used to extract\n  // schemas into $defs — it is not user-facing JSON Schema metadata. Strip it\n  // from the output body where it would otherwise leak. The id is preserved\n  // implicitly via the $defs key (and via $ref paths).\n  const rootMetaId = ctx.metadataRegistry.get(schema)?.id;\n  if (rootMetaId !== undefined && result.id === rootMetaId) delete result.id;\n\n  // build defs object\n  const defs: JSONSchema.BaseSchema[\"$defs\"] = ctx.external?.defs ?? {};\n  for (const entry of ctx.seen.entries()) {\n    const seen = entry[1];\n    if (seen.def && seen.defId) {\n      if (seen.def.id === seen.defId) delete seen.def.id;","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/to-json-schema.ts#L450-L486","documentation":"Thrown near the end of `z.toJSONSchema()` when an `external.uri` generator is configured but the root schema being converted has no registered `id`. The external-URI feature constructs the output `$id` from the schema's id; without one it cannot form the URI. The schema must be registered with an id (via `.meta({ id })` or an external registry) before conversion.","triggerScenarios":"Calling `z.toJSONSchema(schema, { external: { uri: (id) => `https://x.com/${id}.json`, registry } })` where `schema` has no id in the registry and no `.meta({ id })`.","commonSituations":"Setting up cross-document `$ref`s with an external registry but forgetting to tag the root schema; renaming a schema and dropping its id.","solutions":["Add an id to the schema: `schema.meta({ id: 'MyType' })` before converting.","Register the schema in the external registry with an id: `registry.add(schema, { id: 'MyType' })`.","Drop the `external.uri` option if you do not need cross-document `$id` emission."],"exampleFix":"// before\nconst S = z.object({ a: z.string() });\nz.toJSONSchema(S, { external: { uri: (id) => `https://x.com/${id}.json`, registry: new Map() } }); // throws\n\n// after\nconst S = z.object({ a: z.string() }).meta({ id: 'Thing' });\nz.toJSONSchema(S, { external: { uri: (id) => `https://x.com/${id}.json`, registry: new Map() } });","handlingStrategy":"validation","validationCode":"function ensureHasId(schema) {\n  const id = (schema as any)._zod?.def?.meta?.id;\n  if (!id) throw new Error('schema needs .meta({ id }) before external URI conversion');\n}","typeGuard":"function schemaHasId(s, registry): boolean {\n  return !!(registry?.get(s)?.id ?? (s as any)._zod?.def?.meta?.id);\n}","tryCatchPattern":"try {\n  z.toJSONSchema(schema, { external: { uri, registry } });\n} catch (e) {\n  if (e instanceof Error && e.message === 'Schema is missing an `id` property') {\n    // add schema.meta({ id: '...' }) or registry.add(schema, { id }) and retry\n  }\n  throw e;\n}","preventionTips":["Always tag root schemas with .meta({ id }) when using external URI generation.","Register schemas in the external registry with an id at registration time.","Drop the external.uri option if you do not need cross-document $id emission."],"tags":["json-schema","conversion","external","meta"],"analyzedSha":"912f0f51b0ced654d0069741e7160834dca742ee","analyzedAt":"2026-08-03T17:41:55.908Z","schemaVersion":2}