{"id":"1dfe069b2436a50c","repo":"colinhacks/zod","slug":"discriminator-property-string-discriminator-ha","errorCode":null,"errorMessage":"Discriminator property ${String(discriminator)} has duplicate value ${String(value)}","messagePattern":"Discriminator property (.+?) has duplicate value (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v3/types.ts","lineNumber":3206,"sourceCode":"  >(\n    discriminator: Discriminator,\n    options: Types,\n    params?: RawCreateParams\n  ): ZodDiscriminatedUnion<Discriminator, Types> {\n    // Get all the valid discriminator values\n    const optionsMap: Map<Primitive, Types[number]> = new Map();\n\n    // try {\n    for (const type of options) {\n      const discriminatorValues = getDiscriminator(type.shape[discriminator]);\n      if (!discriminatorValues.length) {\n        throw new Error(\n          `A discriminator value for key \\`${discriminator}\\` could not be extracted from all schema options`\n        );\n      }\n      for (const value of discriminatorValues) {\n        if (optionsMap.has(value)) {\n          throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);\n        }\n\n        optionsMap.set(value, type);\n      }\n    }\n\n    return new ZodDiscriminatedUnion<\n      Discriminator,\n      // DiscriminatorValue,\n      Types\n    >({\n      typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,\n      discriminator,\n      options,\n      optionsMap,\n      ...processCreateParams(params),\n    });\n  }","sourceCodeStart":3188,"sourceCodeEnd":3224,"githubUrl":"https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v3/types.ts#L3188-L3224","documentation":"Thrown by z.discriminatedUnion when two or more options resolve to the same discriminator value. The dispatch map keys must be unique or the union cannot pick a branch deterministically.","triggerScenarios":"Two options whose discriminator field uses the same z.literal, or whose literals/enums collapse to the same primitive, e.g. `z.object({ type: z.literal('x') })` appearing twice, or `z.enum(['x','y'])` overlapping another option's `z.literal('x')`.","commonSituations":"Copy-pasting a branch and forgetting to change the tag; merging schemas where tags collide; an option that uses an enum sharing a value with another branch's literal.","solutions":["Make every option's discriminator value(s) mutually exclusive.","If the same payload should validate against multiple shapes, model it as z.intersection or merge the schemas instead of a discriminated union.","List the discriminator values per branch and dedupe."],"exampleFix":"// before\nz.discriminatedUnion('type', [\n  z.object({ type: z.literal('x'), a: z.string() }),\n  z.object({ type: z.literal('x'), b: z.number() }), // duplicate 'x'\n]);\n\n// after\nz.discriminatedUnion('type', [\n  z.object({ type: z.literal('x'), a: z.string() }),\n  z.object({ type: z.literal('y'), b: z.number() }),\n]);","handlingStrategy":"validation","validationCode":"function assertDistinctDiscriminators(disc: string, options: z.ZodObject<any>[]) {\n  const seen = new Set<unknown>();\n  for (const o of options) {\n    const f = o.shape[disc];\n    const values = f instanceof z.ZodLiteral ? [f.value]\n      : f instanceof z.ZodEnum ? f.options : [];\n    for (const v of values) {\n      if (seen.has(v)) throw new Error(`Duplicate discriminator: ${String(v)}`);\n      seen.add(v);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Maintain a single source of truth for tag values (e.g. a z.enum) and derive literals from it.","Add a construction-time test enumerating discriminator values per branch.","Avoid enums in the discriminator field unless all values are mutually exclusive across branches."],"tags":["discriminated-union","duplicate","v3"],"analyzedSha":"912f0f51b0ced654d0069741e7160834dca742ee","analyzedAt":"2026-08-03T17:41:55.908Z","schemaVersion":2}