colinhacks/zod · error · Error
Duplicate discriminator value "${String(v)}"
Error message
Duplicate discriminator value "${String(v)}" What it means
Thrown while building the discriminator map in `$ZodDiscriminatedUnion`. Two different options register the same discrete value for the discriminator key, so the parser could not decide which option to select. Discriminated unions require each option's discriminator value(s) to be unique across the union.
Source
Thrown at packages/zod/src/v4/core/schemas.ts:2393
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,
inst,
});
return payload;View on GitHub (pinned to 912f0f51b0)
Solutions
- Look at the duplicate value in the message and find which two options both permit it; change one of them to a distinct literal/enum value.
- If overlap is intentional, discriminated union is the wrong tool — use `z.union([...])` and disambiguate with `.refine()` instead.
- Audit each option's discriminator field (especially enum/unions of literals) to ensure the sets are disjoint.
Example fix
// before
const U = z.discriminatedUnion('type', [
z.object({ type: z.literal('admin'), perms: z.array(z.string()) }),
z.object({ type: z.literal('admin'), scope: z.string() }), // duplicate 'admin'
]);
// after
const U = z.discriminatedUnion('type', [
z.object({ type: z.literal('admin'), perms: z.array(z.string()) }),
z.object({ type: z.literal('superadmin'), scope: z.string() }),
]); Defensive patterns
Strategy: validation
Validate before calling
function assertUniqueDiscriminatorValues(discriminator, options) {
const seen = new Map();
options.forEach((opt, i) => {
const vals = opt._zod?.propValues?.[discriminator] ?? new Set();
for (const v of vals) {
if (seen.has(v)) throw new Error(`Duplicate discriminator "${String(v)}" at options ${seen.get(v)} and ${i}`);
seen.set(v, i);
}
});
} Try / catch
try {
const U = z.discriminatedUnion('type', options);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Duplicate discriminator value')) {
// parse out the value, locate the two colliding options, make them distinct
}
throw e;
} Prevention
- Give each variant a distinct, descriptive discriminator literal.
- When using z.enum/z.literal unions as the discriminator, verify the value sets are disjoint.
- Lint for repeated literal values across variant definitions in the same union.
When it happens
Trigger: Two options both declaring `type: z.literal('user')`, or two enum-backed discriminator fields whose allowed values overlap (e.g. one `z.enum(['a','b'])` and another `z.enum(['b','c'])` where the shared value is 'b').
Common situations: Adding a new variant and accidentally reusing an existing discriminator value; using unions of literals for the discriminator that overlap between options; merging two separately-defined unions that happen to share a value.
Related errors
- Invalid discriminated union option at index "${def.options.i
- Invalid discriminated union option at index "${def.options.i
- 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/d14797bee0f9e7a2.json.
Report an issue: GitHub.