Skip to content

fix: discriminatedUnion encode() with codec discriminator#5769

Open
mahmoodhamdi wants to merge 1 commit intocolinhacks:mainfrom
mahmoodhamdi:fix/discriminated-union-encode-codec
Open

fix: discriminatedUnion encode() with codec discriminator#5769
mahmoodhamdi wants to merge 1 commit intocolinhacks:mainfrom
mahmoodhamdi:fix/discriminated-union-encode-codec

Conversation

@mahmoodhamdi
Copy link

Summary

Fixes #5593

When the discriminator field is a ZodCodec, encode() fails with "No matching discriminator" because the fast discriminator map only contains forward (decode) values while the encode input has backward (output) values.

Before

const codec1 = z.codec(z.literal(1), z.literal("one"), {
  decode: () => "one" as const,
  encode: () => 1 as const,
});
const schema = z.discriminatedUnion("type", [
  z.object({ type: codec1, value: z.string() }),
  // ...
]);
z.encode(schema, { type: "one", value: "hello" }); // ❌ "No matching discriminator"

After

z.encode(schema, { type: "one", value: "hello" }); // ✅ { type: 1, value: "hello" }

Approach

The discriminator map is precomputed from forward propValues — for codecs, these are the input schema values (1, 2). During encode(), the discriminator comes from the output schema ("one", "two"), so the fast-path lookup misses.

Rather than building a second backward discriminator map (which adds complexity and may not generalize to all codec patterns), this fix falls back to the union matching path when the fast discriminator lookup fails in backward direction. This is the same path that unionFallback: true already uses.

Files changed

  • packages/zod/src/v4/core/schemas.ts — 3-line change in $ZodDiscriminatedUnion.parse
  • packages/zod/src/v4/classic/tests/discriminated-unions.test.ts — new test case

Test plan

  • New test: encode with codec discriminator passes
  • All existing discriminated union tests pass (23/23)
  • Full test suite passes (3577 tests, 0 type errors)
  • Format and lint checks pass

When the discriminator field is a ZodCodec, the forward discriminator
values (used during decode) differ from the backward values (used during
encode). The fast discriminator map only contains forward values, so
encode() fails with "No matching discriminator".

Fall back to union matching when the fast-path fails in backward
direction, allowing codec-based discriminators to work correctly.

Fixes colinhacks#5593
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

discriminatedUnion fails on encode() when discriminator is a ZodCodec

1 participant