colinhacks/zod · error · Error
Invalid discriminated union option at index "${def.options.i
Error message
Invalid discriminated union option at index "${def.options.indexOf(o)}" What it means
Thrown while building the discriminator lookup map in `$ZodDiscriminatedUnion` (the cached `disc` function). The option at the given index is an object, but it has no values registered for the specific discriminator key (`def.discriminator`). In other words, the option is discriminable in general but does not actually carry the discriminator field that the union was declared with, so it cannot be matched during parsing.
Source
Thrown at packages/zod/src/v4/core/schemas.ts:2390
if (!pv || Object.keys(pv).length === 0)
throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(option)}"`);
for (const [k, v] of Object.entries(pv!)) {
if (!propValues[k]) propValues[k] = new Set();
for (const val of v) {
propValues[k].add(val);
}
}
}
return propValues;
});
const disc = util.cached(() => {
const opts = def.options as $ZodTypeDiscriminable[];
const map: Map<util.Primitive, $ZodType> = new Map();
for (const o of opts) {
const values = o._zod.propValues?.[def.discriminator];
if (!values || values.size === 0)
throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`);
for (const v of values) {
if (map.has(v)) {
throw new Error(`Duplicate discriminator value "${String(v)}"`);
}
map.set(v, o);
}
}
return map;
});
inst._zod.parse = (payload, ctx) => {
const input = payload.value;
if (!util.isObject(input)) {
payload.issues.push({
code: "invalid_type",
expected: "object",
input,View on GitHub (pinned to 912f0f51b0)
Solutions
- Check the option at the reported index and add the discriminator field with a literal/enum value, e.g. `kind: z.literal('circle')`.
- Verify the discriminator string passed to `z.discriminatedUnion(discriminator, options)` exactly matches the field name present on EVERY option.
- Make the discriminator field an enum or literal on each option (a plain `z.string()` produces no discrete values and is not discriminable).
Example fix
// before
const Shape = z.discriminatedUnion('kind', [
z.object({ kind: z.literal('circle'), radius: z.number() }),
z.object({ shape: z.literal('square'), side: z.number() }), // wrong key
]);
// after
const Shape = z.discriminatedUnion('kind', [
z.object({ kind: z.literal('circle'), radius: z.number() }),
z.object({ kind: z.literal('square'), side: z.number() }),
]); Defensive patterns
Strategy: validation
Validate before calling
function assertAllHaveDiscriminator(discriminator, options) {
options.forEach((opt, i) => {
const pv = opt._zod?.propValues;
if (!pv || !pv[discriminator] || pv[discriminator].size === 0) {
throw new Error(`Option ${i} missing discriminator "${discriminator}"`);
}
});
} Type guard
function hasDiscriminatorValue(s, key): boolean {
const pv = s._zod?.propValues;
return !!pv && pv[key] instanceof Set && pv[key].size > 0;
} Try / catch
try {
const U = z.discriminatedUnion(discKey, options);
} catch (e) {
if (e instanceof Error && e.message.includes('Invalid discriminated union option')) {
// identify the option missing the discriminator key and fix its shape
}
throw e;
} Prevention
- Keep the discriminator key name in a shared constant and reference it on every option.
- Type each option's discriminator field as z.literal(...) or z.enum([...]), never a bare z.string().
- Add a test that parses one example per option to surface definition errors.
When it happens
Trigger: Declaring `z.discriminatedUnion('kind', [A, B])` where the discriminator key is `'kind'` but one of A/B has no `kind` field, or its `kind` field is a non-literal type like `z.string()` (which yields no discrete propValues). Also: a typo in the discriminator string, or renaming the field on one option but not the others.
Common situations: Renaming a discriminator field on one variant and forgetting the others; copy-pasting variants and changing the field name only in the copy; using `z.string()` for the discriminator instead of `z.literal`/`z.enum`.
Related errors
- Invalid discriminated union option at index "${def.options.i
- Duplicate discriminator value "${String(v)}"
- Cannot create literal schema with no valid values
- Invalid template literal part: ${part}
- A discriminator value for key `${discriminator}` could not b
AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03).
Data as JSON: /data/errors/c97358a769ab77be.json.
Report an issue: GitHub.