Preserve callsites in parse stack traces#5910
Conversation
|
Reviewed PR #5910 — clean implementation that extends Task list (3/3 completed)
|
|
TL;DR — When a Zod parse/encode/decode call throws, the first stack frame now points to the caller's code rather than Zod internals. This is achieved by giving each parse function a stable reference that Key changes
Summary | 4 files | 1 commit | base: Stable callsite preservation via named function references
The key insight is that
|
There was a problem hiding this comment.
Reviewed — no issues found.
Task list (3/3 completed)
- Checkout PR and read the diff
- Investigate major areas of change
- Self-critique and submit review
Claude Opus | 𝕏

Closes #3254.
This keeps Zod's existing best-effort
Error.captureStackTracebehavior, but applies it consistently to the public throwing parse paths. Top-levelz.parse/z.parseAsyncnow trim their own core parse frame, andencode/decodesync and async paths pass the same callsite boundary that instance.parse()already used.I also added regression coverage for instance, detached, and top-level parse plus codec methods so the first stack frame stays at the user callsite instead of
core/parse.tsorclassic/schemas.ts.Nested stack probes were run from a temporary fixture at
/tmp/zod-stack-probe/scenario.tsto simulate validation/serialization inside app call chains.Before this change,
schema.parse()already started at the user validation function, but top-level parse and codec paths exposed Zod internals first:After this change, those same nested scenarios start at the user's validation/serialization function and preserve the surrounding app frames:
Validated with
pnpm vitest run packages/zod/src/v4/classic/tests/error.test.ts,pnpm vitest run packages/zod/src/v4,pnpm test, andpnpm build. The push hook also reran the fullpnpm testsuite successfully.Moltar was run against unchanged
mainand this branch on Node v24.14.0 / Apple M1 Max. The clean baseline run reportedzod4at 179 us/iter forz.object() safeParse; the clean branch run reported 184 us/iter. Repeat runs were noisy on both trees, including an unchanged-main repeat at 474 us/iter, so I don't see a meaningful regression signal here. This patch does not change thesafeParseimplementation that Moltar exercises.I also ran an inline ad hoc benchmark, without writing a benchmark file, against the paths this PR actually changes. The initial short run was too noisy to trust, so I reran the failure cases with 8 rounds of 50k iterations and compared medians:
The longer success-path run was also in the same range for the paths measured. The only suspicious result was one branch run of
top-level parse failat 16971 ns/iter, but the repeated 8-round run did not reproduce it. I don't see a consistent regression signal from either Moltar or the targeted failure-path benchmark.