Skip to content

fix(v4): prevent superRefine suppression on missing optional fields (#5653)#5658

Open
tushargr0ver wants to merge 1 commit intocolinhacks:mainfrom
tushargr0ver:fix/5653-optional-superrefine
Open

fix(v4): prevent superRefine suppression on missing optional fields (#5653)#5658
tushargr0ver wants to merge 1 commit intocolinhacks:mainfrom
tushargr0ver:fix/5653-optional-superrefine

Conversation

@tushargr0ver
Copy link
Contributor

Fixes #5653

Description

This PR fixes a regression in v4.3.0 where superRefine errors on optional() fields were suppressed if the field was missing from the input object.

The issue was caused by ZodObject suppressing errors for missing keys if optout was "optional". This behavior is correct for exactOptional (where missing keys are valid but explicit undefined is not), but incorrect for standard optional fields which handle undefined internally (and should surface errors from refinements).

Changes

  • Updated ZodExactOptional to use optout="exactOptional" at runtime.
  • Updated ZodObject (standard and JIT) to only suppress errors if optout === "exactOptional".
  • Updated ZodUnion to correctly infer optout status.
  • Added reproduction test case.

Notes

To avoid breaking public types and massive variance issues, optout remains typed as "optional" | undefined in the interface, but we cast to any internally to assign/check "exactOptional".

Copy link
Contributor

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, minimal fix for a real regression. The approach of using a runtime-only "exactOptional" value while preserving the public type signature is pragmatic and avoids breaking changes. Tests pass and the logic is sound.

Approving with one minor observation (non-blocking): the handleCatchall function still uses optout === "optional" (line 1812), but this is actually fine because for catchall, we iterate over keys that are already present in the input, so the !(key in input) check in handlePropertyResult would always be false—errors are never suppressed for catchall regardless.

Pullfrog  | View workflow runpullfrog.com𝕏

@pullfrog
Copy link
Contributor

pullfrog bot commented Jan 23, 2026

Review complete

Reviewed PR #5658 which fixes a regression where superRefine errors on optional() fields were suppressed when the field was missing from input.

Summary: The fix correctly distinguishes exactOptional() from optional() at runtime, ensuring that only exactOptional fields suppress errors for missing keys. The approach is sound and all tests pass.

Pullfrog  | Triggered by Pullfrog | Using Claude CodeView workflow runpullfrog.com𝕏

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.

superRefine() errors no longer surfaced if used in combination with optional() on an object() field that is not defined

1 participant