Skip to content

zod v4: .string().min() (checks) crashes — ch._zod.onattach undefined under compilePackages #4698

Closed

Description

@proggeramlug

Repro

package.json: { "perry": { "compilePackages": ["zod"], "allow": { "compilePackages": ["zod"] } } }

import { z } from "zod";
console.log(z.string().min(2).parse("hello"));

Perry:

TypeError: Cannot read properties of undefined (reading 'onattach')
    at <anonymous>

Node (--experimental-strip-types): hello

Any schema with a check (.min, .max, .length, .regex, .email, numeric .gt/.lt, …) hits this — z.string()/z.object().parse() without checks works.

Analysis

In zod's $ZodType initializer (node_modules/zod/src/v4/core/schemas.ts):

const checks = [...(inst._zod.def.checks ?? [])];
for (const ch of checks) {
  for (const fn of ch._zod.onattach) { fn(inst); }   // ch._zod is undefined
}

A check instance in def.checks comes back without _zod. The check is built by new $ZodCheckMinLength({...}) (a $constructor that installs _zod via Object.defineProperty(inst, "_zod", { value, enumerable:false })), threaded through inst.check() → util.mergeDefs(def, { checks:[...] }) (which round-trips via Object.getOwnPropertyDescriptors + Object.defineProperties) → core.clone().

Not reproducible standalone: a faithful standalone model of the entire $constructor + $ZodCheck.init + onattach.push + mergeDefs (getOwnPropertyDescriptors/defineProperties round-trip) + [...def.checks] spread chain runs correctly in Perry (byte-matches Node). So the _zod loss is specific to the cross-module perry.compilePackages compilation of real zod — a check instance constructed in core/checks.ts and read back through core/schemas.ts / core/util.ts loses its non-enumerable _zod property somewhere across the module boundary.

Context

Follow-up to discussion #3438. The core object-parse crash (reading '_zod' of undefined) is fixed by #4697 (Object.defineProperty attribute retention); with that fix, z.object() / nested / array / boolean / optional parsing works. This checks-path bug and the safeParse error-path bug (separate issue) remain. Refs #793.

Activity

  1. added
    bugConfirmed defect or regression
    parityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem
    on Jun 6, 2026
  2. paulo-assoc commented on Jun 7, 2026

    @paulo-assoc

    I am blocked by this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions