Repository navigation
gc: zod dep-corpus under PERRY_GC_SCHEDULE_RATE=1 with the quarantine OFF dies with a rooting-shaped TypeError (seed 1, safepoint 3319) #7803
Description
Activity
- added 3 commits that reference this issue
on Aug 12, 2026 Status after working #7963 / #7964 (PR #7978):
Still not reproducible —
test-files/gc-dep-corpus/main.tsdoes not link. I confirmed the failure again on a build oforigin/main(a769faf,PERRY_NO_AUTO_OPTIMIZE=1,PERRY_RUNTIME_DIRpinned): 45 distinct undefined_perry_fn_node_modules_zod_…symbols. The verdict on #7964 is genuine compiler gap (named re-export from anexport *barrel emits a reference the barrel never defines), not a stale pin — details in that issue. So the blocker stands.PR #7978 does not unblock it. That fix is runtime-side (
Object.defineProperty's receiver / key /DescViewrooting); this failure is in module lowering / codegen.The candidate connection is now a little stronger, and still only a candidate. zod's descriptor traffic runs through both helpers in this family:
Object.definePropertiesatsrc/v4/core/util.ts:316andsrc/v4/classic/errors.ts:28— the helper runtime: root JSValues retained in Rust containers across allocations #7949/fix(runtime): root JS values retained in Rust containers across allocations (#7949) #7962 fixed;Object.defineProperty— the window fix(gc): root Object.defineProperty's receiver, key and descriptor fields across its own allocating calls #7978 fixes, where a pristine build faults on a retired from-space receiver (obj_type=2) under the quarantine, and where the stale receiver address is also the OWNER KEY of the per-property attribute/accessor side tables. A define filed under a dead address leaves the property invisible to every later read.
#7803's symptom —
Cannot read properties of undefined (reading 'toString')— is exactly what either a stale key string or a stale receiver in those loops produces: the property is defined under a garbage name or a dead owner, and the later read misses.Retest plan once #7964 lands: build the corpus, then run the two arms named in this issue (
PERRY_GC_PROTECT_FROMSPACE=0vs=1, seed 1, rate 1) against a build with #7978 in it and against one without. If #7978 closes it, the unprotected arm goes green; if not, the seed sweep over the protected arm (scripts/gc_schedule_fuzz.sh) is still the way to turn it into a precise fault.- added a commit that references this issue
on Aug 12, 2026 Retested on
bdfcba4a2(v0.5.1499). Short version: the reproducer is stale, the subject is not. Please don't close.The corpus was unrunnable all week (#7964); #7980 (
fec46413d) fixed it and
test-files/gc-dep-corpus/main.tsnow compiles and links (exit 0, 29.9 MB), on
the corpus andzod@4.3.5pin already in the tree — nothing regenerated or
re-pinned.1. The filed reproducer no longer fails
PERRY_GC_SCHEDULE_SEED=1 PERRY_GC_SCHEDULE_RATE=1 PERRY_GC_PROTECT_FROMSPACE=0 PERRY_GC_DIAG=1
→ exit 0, 4 runs out of 4 (3 dedicated + seed 1 inside the sweep below), stdout byte-identical to the unscheduled run, with
the instrument live rather than assumed (6,627–6,909 copying minors, ~872k
objects moved, 63,936 loop polls per run).2. …but the class is still live: 3 of 16 seeds fail
RATE=1 TIMEOUT=1800 PERRY_GC_PROTECT_FROMSPACE=0 PERRY_GC_DIAG=1 scripts/gc_schedule_fuzz.sh /tmp/w 16seed failure safepoint 4 TypeError: value is not a function738 15 exit 134, [gc-pin-latch] FATAL1870 16 TypeError: Cannot read properties of undefined (reading 'issues')626 Seed 16 is the same message shape as the one you filed. Seed 15 is a different
bug and is now #7990.3. The seeded schedule does NOT replay — this changes how the issue should be read
You asked for determinism to be confirmed first. It does not hold: at a fixed
seed,loop_pollsis stable (63,936) butsafepoints/scheduled_collections
drift ~4% run to run (6627 → 6652 → 6909). Consequently every failure here is
intermittent:seed in the sweep on re-run, same command 4 FAIL pass, 2/2 15 FAIL pass, 3/3 1 (this issue's) pass pass, 4/4 So §1 is a coin landing the other way, not a repair. The durable figure is the
rate: ~19% of runs (3/16).4. The candidate cause is refuted
gc-handoff/ROOTVEC-NOTES.mdnamed #7962/#7978 (Object.defineProperties/
Object.definePropertyrooting) as a candidate, explicitly unconfirmed. Sabotage
A/B — both fixes reverted underneath currentmain, archives verified to have
moved, relinked binary verified different:arm seed 1, rate 1, quarantine OFF mainexit 0 ×3 #7962 + #7978 reverted exit 0 ×2 Reverting them does not bring it back. They are not what closed the seed-1
window. (Tree restored and rebuilt afterwards, notgit checkout-ed.)The workload is not the variable either:
test-files/gc-dep-corpus/is untouched
since #7311 and the installed zod is 4.3.5 throughout, so the only delta between
v0.5.1458 and v0.5.1499 is compiler + runtime.5. Your hypothesis (1) is confirmed on a second seed
Seed 4 fails unprotected at safepoint 738; the same seed with the quarantine on
at depth 800 passes, running 6,888 copying minors, with the instrument
saturated (sets_held=800/800 bytes_protected=2095054848, 6,888 retired sets).
Holding retired from-space pages out of Eden changes which addresses get
recycled and the window stops lining up. The protected arm is therefore not
evidence of health here, andscripts/gc_dep_scale_witness.sh(quarantine ON)
structurally cannot catch this window on this workload.I ran your suggested way out — a seed sweep over the protected arm — and it
has not produced a protected-arm fault so far, so that route did not pay off in
this session.Suggested disposition
Keep open; retitle away from "seed 1, safepoint 3319" and toward the rate, or
close in favour of a fresh issue quoting §2. Full audit trail, including the
things that did not work, is ingc-handoff/ZOD-NOTES.md(#7989).Unrelated footgun found on the way:
PERRY_GC_DIAG=0enables diagnostics
(telemetry.rs:11usesvar_os(...).is_some()), which silently collapsed one of
my A/B arms.PERRY_GC_PROTECT_FROMSPACEdoes not have this bug, so the=0in
this issue's repro really is off.Closing with causal evidence, not because the old seed-1 reproducer merely stopped firing.
The surviving population was localized to comparison operands in the zod new-Function/dyn_eval path. #8011 (main commit 0543e87) roots the left comparison operand across evaluation of the allocating right operand. Its causal A/B moved from 3 failures in 16 saturated quarantine-off runs to 26/26 passes after the fix, each under PERRY_GC_SCHEDULE_RATE=1 with roughly 6,400 copying minors and ~855k moved objects—so the stress subject was live, not inferred.
#8021 (main commit 410dadd) then pinned the exact typed-Map comparison shape as
strict_eq_rereads_a_map_operand_below_an_allocating_right_operand; current main a9a99d8 contains that emitted-IR regression and the registered corpus witnesses.This also accounts for the incoherent Map header reported separately in #7990. The old fixed-seed wording remains historically stale because schedule counts drift, but the defect class is now causally fixed and guarded.
- added 4 commits that reference this issue
on Aug 14, 2026 8 remaining items
- added 15 commits that reference this issue
on Aug 14, 2026
Found by #7793's witness during its merge audit, in the sabotage arm that turns the from-space quarantine OFF — i.e., this is a latent defect on
main(v0.5.1458 runtime), not a defect of that PR, and the shipped witness configuration (quarantine ON) runs the same workload clean.Reproduce
Observed (M1 Pro, macOS, release build):
exit 1, at safepoint 3,319 of the ~8,000 the full run performs. The workload is single-threaded, so the seeded schedule should replay exactly (
(seed, counter)— one observation so far; the box was at load 20+ and a second confirmation run was cut off, so start by confirming determinism).Why this smells like a #7154-class rooting bug and not an instrument bug
PERRY_GC_PROTECT_FROMSPACE=1(depth 800) passes: 8,022 copying minors, 852,411 objects moved, output byte-identical to the unscheduled run — verified twice.TypeErroron anundefinedthat should be an object, cycles after the collection that lost it.PERRY_GC_SCHEDULE_SEEDsweeps over the PROTECTED arm (scripts/gc_schedule_fuzz.sh) should eventually find a seed whose protected run faults precisely — that would hand us the exact stale address and retiring minor.Context
The zod dependency corpus is exactly the population #7280 showed the curated fixtures don't reach (
js_object_assign_one,js_new_function_construct). #7793's shipped witness gates the protected arm in CI; this issue tracks the unprotected finding it flushed out on its first run.