Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/zod/src/v4/core/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4166,8 +4166,8 @@ export const $ZodLazy: core.$constructor<$ZodLazy> = /*@__PURE__*/ core.$constru
util.defineLazy(inst._zod, "innerType", () => def.getter() as $ZodType);
util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType._zod.pattern);
util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType._zod.propValues);
util.defineLazy(inst._zod, "optin", () => inst._zod.innerType._zod.optin ?? undefined);
util.defineLazy(inst._zod, "optout", () => inst._zod.innerType._zod.optout ?? undefined);
util.defineLazy(inst._zod, "optin", () => inst._zod.innerType?._zod.optin ?? undefined);
util.defineLazy(inst._zod, "optout", () => inst._zod.innerType?._zod.optout ?? undefined);
Comment on lines +4169 to +4170
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Inconsistent optional chaining - consider applying to all innerType accesses.

The optional chaining here prevents the TypeError for optin and optout, but lines 4167-4168 directly above access innerType._zod.pattern and innerType._zod.propValues without the same protection. If innerType can be undefined during lazy evaluation (which this PR fixes), shouldn't all property accesses be guarded consistently?

Based on learnings, wrapper schemas should pass through propValues, values, optin, and optout similarly. The partial fix might leave other code paths vulnerable to the same error.

Consider applying optional chaining to lines 4167-4168 as well:

-  util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType._zod.pattern);
-  util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType._zod.propValues);
+  util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType?._zod.pattern);
+  util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType?._zod.propValues);
   util.defineLazy(inst._zod, "optin", () => inst._zod.innerType?._zod.optin ?? undefined);
   util.defineLazy(inst._zod, "optout", () => inst._zod.innerType?._zod.optout ?? undefined);

Run this script to verify how pattern and propValues are used across other wrapper schemas:


🏁 Script executed:

#!/bin/bash
# Check how other wrapper schemas handle innerType property access
rg -n -A2 -B2 'defineLazy.*\b(pattern|propValues)\b' packages/zod/src/v4/core/schemas.ts

Length of output: 2567



I need to see the actual code context around the parse function to understand how it handles innerType.

---

🏁 Script executed:

```shell
#!/bin/bash
# View the complete $ZodLazy constructor including parse function and all property definitions
sed -n '4160,4180p' packages/zod/src/v4/core/schemas.ts

Length of output: 78


🏁 Script executed:

#!/bin/bash
# View the complete $ZodLazy constructor including parse function and all property definitions
sed -n '4160,4180p' packages/zod/src/v4/core/schemas.ts

Length of output: 876


Incomplete fix — optional chaining needed on pattern and propValues too.

The PR adds optional chaining for optin and optout (lines 4169-4170), but lines 4167-4168 directly above still access innerType._zod.pattern and innerType._zod.propValues without protection. Since innerType is lazily computed here (line 4166 via def.getter()), it could be undefined during evaluation, making pattern and propValues vulnerable to the same TypeError.

The parse function (lines 4172-4173) also accesses innerType without guards. For consistency and safety, all these accesses should be protected:

-  util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType._zod.pattern);
-  util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType._zod.propValues);
+  util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType?._zod.pattern);
+  util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType?._zod.propValues);
   util.defineLazy(inst._zod, "optin", () => inst._zod.innerType?._zod.optin ?? undefined);
   util.defineLazy(inst._zod, "optout", () => inst._zod.innerType?._zod.optout ?? undefined);

Also verify the parse function (lines 4172-4173) won't crash if innerType is undefined.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/zod/src/v4/core/schemas.ts around lines 4167 to 4173, accesses to
inst._zod.innerType._zod.pattern and inst._zod.innerType._zod.propValues (and
the parse function that uses innerType) are not protected and can throw if
innerType is undefined; update those lines to use optional chaining (e.g.,
innerType?._zod?.pattern and innerType?._zod?.propValues) and add a guard in the
parse function to handle the case when innerType is undefined (return
appropriate default/failure or skip inner parsing) so no property access occurs
on undefined.

inst._zod.parse = (payload, ctx) => {
const inner = inst._zod.innerType;
return inner._zod.run(payload, ctx);
Expand Down
Loading