fix: Handle z.union([]) and z.xor([])#5869
Conversation
`z.union([])`, `z.xor([])`, and `z.discriminatedUnion(_, [])` (plus their `zod/mini` counterparts) all crashed at construction time with `Cannot read properties of undefined (reading '_zod')` because the $ZodUnion / $ZodXor initializers eagerly read `def.options[0]._zod.run`. Guard the cached `first` runner so it's only captured when there's exactly one option. The existing `handleUnionResults` / `handleExclusiveUnionResults` helpers already do the right thing for zero results: they fall through to `invalid_union` with `errors: []`. $ZodDiscriminatedUnion inherits the construction fix transitively through its $ZodUnion.init call, and its own parse path already emits an `invalid_union` for unmatched discriminators. Empty unions now construct successfully and reject every input at parse time, matching the type-level inference of `never`.
|
Thanks for the report and the PR. Pushed a different fix on top — wanted to flag what changed and why. Approach: instead of adding a special-case Why not Coverage: also fixes Going to merge as-is.
|
|
Reviewed PR #5869. Approved — the fix correctly prevents the out-of-bounds access on empty union/xor options by combining the Task list (5/5 completed)
|
|
TL;DR — Key changes
Summary | 3 files | 1 commit | base: Empty union/xor no longer crashes at parse time
The fix is a one-line change applied identically in both
|
There was a problem hiding this comment.
Reviewed — no issues found.
Clean fix. Combining single and first into a single conditional assignment is the right approach — it avoids the out-of-bounds access on def.options[0] when the array is empty, while preserving the single-option fast path. The empty-results case flows correctly through both handleUnionResults (produces invalid_union with errors: []) and handleExclusiveUnionResults (same). Test coverage is thorough.
Task list (5/5 completed)
- Read the PR diff and understand the changes
- Read the affected source file (schemas.ts) for context
- Trace data flow and verify correctness
- Check for edge cases and potential issues
- Submit review
Claude Opus | 𝕏
|
Landed in Zod 4.4 |
The types already resolve to
never, butparsewould throw.Think it makes sense to treat as
neverwhen parsing?Fixes #5868