zod icon indicating copy to clipboard operation
zod copied to clipboard

fix(transform): abort further processing when inner value is invalid

Open DASPRiD opened this issue 7 months ago • 3 comments

Previously when an inner value was invalid, transforms would not run, but subsequent refinements would still run with the non-transformed value, which in most cases would violate the typings.

This fix returns INVALID from the transform in case of a failed inner validation and stop outer refinements from running with the wrong types.

Closes #2243 Closes #2192 Closes #2113

Summary by CodeRabbit

  • Tests
    • Added a new test to verify behavior when a validation transform is skipped and an additional refinement is applied.
  • Bug Fixes
    • Improved handling of invalid parse results during schema transformations to ensure consistent error reporting.

DASPRiD avatar Apr 23 '25 10:04 DASPRiD

Walkthrough

This update modifies the internal parsing logic for schema effects, specifically in how invalid intermediate results are handled during transforms. Now, when a validation fails before a transform, the parser returns a standardized INVALID constant rather than the original invalid result object. Additionally, new tests have been added to verify that the superRefine function is still called even when a transform is skipped due to earlier validation failure, ensuring consistent behavior across both test directories.

Changes

File(s) Change Summary
src/types.ts, deno/lib/types.ts Updated ZodEffects._parse to return INVALID instead of the original invalid result when inner schema validation fails in a transform effect. Applies to both sync and async branches. No public API changes.
src/tests/refine.test.ts, deno/lib/tests/refine.test.ts Added test "superRefine after skipped transform" to ensure superRefine is executed even if a transform is skipped due to validation failure.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Schema
    participant Transform
    participant SuperRefine

    User->>Schema: parse(input)
    Schema->>Schema: validate input (e.g., regex)
    alt Validation fails
        Schema-->>Transform: skip transform
        Schema->>SuperRefine: call superRefine with original input
        SuperRefine-->>Schema: add issues if needed
        Schema-->>User: return INVALID result
    else Validation passes
        Schema->>Transform: apply transform
        Transform->>SuperRefine: call superRefine with transformed value
        SuperRefine-->>Schema: add issues if needed
        Schema-->>User: return result
    end

Assessment against linked issues

Objective Addressed Explanation
Prevent refine/superRefine from running when parser already failed (#2192, #2113, #2243)
Ensure superRefine is still called after skipped transform, but with correct input (#2243)
Avoid passing invalid or unexpected types to refine/superRefine when validation fails (#2192, #2113)

Poem

When transforms are skipped, but checks still remain,
The parser now signals with INVALID—so plain.
superRefine stands ready, no matter the fate,
Ensuring all rules, even after a gate.
With tests now in place and bugs out the door,
The schema’s more solid than ever before!
🛡️✨


📜 Recent review details

Configuration used: CodeRabbit UI Review profile: ASSERTIVE Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e3b4ed030c2df1d172e544d5408aa157526eefd and 354538fd45dd0d92b232e812d54a209551ff7e36.

📒 Files selected for processing (4)
  • deno/lib/__tests__/refine.test.ts (1 hunks)
  • deno/lib/types.ts (2 hunks)
  • src/__tests__/refine.test.ts (1 hunks)
  • src/types.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
deno/lib/types.ts (1)
deno/lib/helpers/parseUtil.ts (3)
  • isValid (187-188)
  • INVALID (166-166)
  • INVALID (167-169)
src/types.ts (1)
src/helpers/parseUtil.ts (3)
  • isValid (187-188)
  • INVALID (166-166)
  • INVALID (167-169)
🔇 Additional comments (6)
deno/lib/types.ts (2)

4727-4727: Handle invalid inner parse for sync transform
The new guard if (!isValid(base)) return INVALID; correctly aborts further processing when the inner parse is invalid in the synchronous branch, ensuring transform effects don’t run on invalid data.


4741-4741: Handle invalid inner parse for async transform
Matching the synchronous logic, the async branch now also returns INVALID if the base parse fails, keeping behavior consistent across both modes.

src/types.ts (2)

727-728: Good fix to standardize error handling! 👍

You're now returning the canonical INVALID constant instead of the original invalid result when inner validation fails. This makes the transform failure behavior more consistent and prevents further processing with incorrect types.


741-742: Same fix applied to the async code path - nice!

Consistent handling in both synchronous and asynchronous branches. This helps maintain predictable behavior regardless of how the parsing is performed.

src/__tests__/refine.test.ts (1)

308-322: Great test coverage for the fix! ✨

This new test verifies that superRefine runs even when a transform is skipped due to validation failure. It confirms the fix works as expected by trying to parse an empty string (which fails regex validation), causing the transform to be skipped, and ensuring proper behavior of the overall validation chain.

deno/lib/__tests__/refine.test.ts (1)

309-323: Solid test mirroring for Deno environment! 👌

Same test as added to the Node environment, ensuring consistent behavior across both runtimes. This is important for maintaining feature parity between the Node and Deno versions of the library.

✨ Finishing Touches
  • [ ] 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot] avatar Apr 23 '25 10:04 coderabbitai[bot]

Deploy Preview for guileless-rolypoly-866f8a ready!

Built without sensitive environment variables

Name Link
Latest commit 354538fd45dd0d92b232e812d54a209551ff7e36
Latest deploy log https://app.netlify.com/sites/guileless-rolypoly-866f8a/deploys/6808bd00e9593e0008073d08
Deploy Preview https://deploy-preview-4266--guileless-rolypoly-866f8a.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

netlify[bot] avatar Apr 23 '25 10:04 netlify[bot]

@colinhacks Anything blocking this fix from being merged?

DASPRiD avatar May 06 '25 18:05 DASPRiD

Thanks for the excellent PR and apologies it took so long. The codebase has changed under this PR so I ported this fix here: https://github.com/colinhacks/zod/pull/4554

colinhacks avatar May 28 '25 22:05 colinhacks

Landed in [email protected]

colinhacks avatar May 29 '25 07:05 colinhacks