{"record":{"id":"c97358a769ab77be","repo":"colinhacks/zod","slug":"invalid-discriminated-union-option-at-index-def-c97358","errorCode":null,"errorMessage":"Invalid discriminated union option at index \"${def.options.indexOf(o)}\"","messagePattern":"Invalid discriminated union option at index \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v4/core/schemas.ts","lineNumber":2407,"sourceCode":"        if (!pv || Object.keys(pv).length === 0)\n          throw new Error(`Invalid discriminated union option at index \"${def.options.indexOf(option)}\"`);\n        for (const [k, v] of Object.entries(pv!)) {\n          if (!propValues[k]) propValues[k] = new Set();\n          for (const val of v) {\n            propValues[k].add(val);\n          }\n        }\n      }\n      return propValues;\n    });\n\n    const disc = util.cached(() => {\n      const opts = def.options as $ZodTypeDiscriminable[];\n      const map: Map<util.Primitive, $ZodType> = new Map();\n      for (const o of opts) {\n        const values = o._zod.propValues?.[def.discriminator];\n        if (!values || values.size === 0)\n          throw new Error(`Invalid discriminated union option at index \"${def.options.indexOf(o)}\"`);\n        for (const v of values) {\n          if (map.has(v)) {\n            throw new Error(`Duplicate discriminator value \"${String(v)}\"`);\n          }\n          map.set(v, o);\n        }\n      }\n      return map;\n    });\n\n    inst._zod.parse = (payload, ctx) => {\n      const input = payload.value;\n      if (!util.isObject(input)) {\n        payload.issues.push({\n          code: \"invalid_type\",\n\n          expected: \"object\",\n          input,","sourceCodeStart":2389,"sourceCodeEnd":2425,"githubUrl":"https://github.com/colinhacks/zod/blob/2d90846af918af9602e088812d63a035d47cdbe4/packages/zod/src/v4/core/schemas.ts#L2389-L2425","documentation":"Thrown inside the cached disc() function of a discriminated union when, for a given option o, `o._zod.propValues?.[def.discriminator]` is absent or empty. Unlike error 42 (which fires when an option has no propValues at all), this fires specifically when the option does have propValues but none are published under the configured discriminator key — i.e. the option exists but does not declare a literal value at that exact key.","triggerScenarios":"Calling z.discriminatedUnion(\"kind\", [...]) where at least one option's object shape lacks a literal at the \"kind\" field, or declares it as a non-literal type (z.string() instead of z.literal()). Also triggered by typos in the discriminator key name (\"type\" vs \"kind\"), or by an option whose discriminator field is optional (z.literal(\"a\").optional()) so propValues may be treated as not contributing a value.","commonSituations":"Discriminator key renamed in one branch but not others; one option omits the discriminator field entirely; field declared as z.string() instead of z.literal()/z.enum(); copy-paste of option schemas where the literal was forgotten; optional discriminator fields.","solutions":["Make sure every option has a literal value (z.literal() or z.enum()) at the exact discriminator key.","Double-check the discriminator key spelling matches across all options.","Avoid marking the discriminator field .optional(); every option must commit to a concrete literal.","Print def.discriminator and each option's propValues keys to locate the mismatched branch."],"exampleFix":"// before\nconst U = z.discriminatedUnion(\"type\", [\n  z.object({ type: z.literal(\"circle\"), r: z.number() }),\n  z.object({ type: z.string(), w: z.number(), h: z.number() }), // not a literal\n]);\n// after\nconst U = z.discriminatedUnion(\"type\", [\n  z.object({ type: z.literal(\"circle\"), r: z.number() }),\n  z.object({ type: z.literal(\"rect\"), w: z.number(), h: z.number() }),\n]);","handlingStrategy":"validation","validationCode":"function buildDiscUnion(disc: string, options: z.ZodObject[]) {\n  for (const o of options) {\n    const vals = (o as any)._zod.propValues?.[disc];\n    if (!vals || vals.size === 0) {\n      throw new Error(`Option is missing a literal at discriminator key \"${disc}\".`);\n    }\n  }\n  return z.discriminatedUnion(disc, options);\n}","typeGuard":"function optionHasDiscriminator(option: unknown, disc: string): boolean {\n  const vals = (option as any)?._zod?.propValues?.[disc];\n  return !!vals && vals.size > 0;\n}","tryCatchPattern":null,"preventionTips":["Ensure every option declares z.literal()/z.enum() at the exact discriminator key.","Keep discriminator keys consistent across options — single source of truth for the key name.","Never mark the discriminator field optional."],"tags":["discriminated-union","schema-construction","discriminator","literal"],"backgroundTag":null,"analyzedSha":"2d90846af918af9602e088812d63a035d47cdbe4","analyzedAt":"2026-08-11T01:21:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}