test-files/test_gap_9592_child_timeout_threads.ts:21 spawns /bin/true:
close(spawn("/bin/true", [], { stdio: "ignore", timeout: 60_000 })),
/bin/true does not exist on macOS — it lives at /usr/bin/true. /bin/sleep (line 37)
is present, so that arm is fine; /bin/true is the only offender.
Effect: the ORACLE fails, not Perry
Node crashes on the unhandled 'error' event and exits 1, while Perry prints the correct
answer and exits 0:
$ node --experimental-strip-types test_gap_9592_child_timeout_threads.ts
node:events:487
throw er; // Unhandled 'error' event
^
Error: spawn /bin/true ENOENT
at ChildProcess._handle.onexit (node:internal/child_process:286:19)
errno: -2, code: 'ENOENT', syscall: 'spawn /bin/true', path: '/bin/true'
Node exit: 1
Perry exit: 0
Node.js: node:events:487
Perry: timeout threads released: true
The harness reports this as PARITY_FAIL (output mismatch) rather than node_fail,
because Node does produce output before dying — so it reads as a Perry regression on
every macOS run. It is not in test-parity/gap_snapshot.json, so on macOS it is an
unlisted red that costs triage time. I hit it while investigating something unrelated
(#10711) and spent a while ruling it out.
Second-order finding: Perry is more lenient than Node here
Perry resolves /bin/true where Node does not — presumably a PATH fallback for an
absolute path that does not exist. That is its own parity gap, independent of the fixture
path: Node's spawn with a non-existent absolute file must emit ENOENT on the child's
'error' event, and Perry apparently finds /usr/bin/true instead. Fixing only the
fixture would hide that. Worth a look while the fixture is being touched, or splitting out
if it deserves its own issue.
Suggested fix
Don't hardcode absolute paths to coreutils in fixtures — the set that exists under /bin
differs across platforms (/bin/true and /bin/false are both absent on macOS; /bin/sh,
/bin/echo, /bin/cat, /bin/sleep, /bin/ls are present). Either spawn by bare name
and let PATH resolve, or use process.execPath with -e "", which is guaranteed present
and portable.
I checked the rest of test-files/: test_gap_9537_child_process_null_bytes.ts also names
/bin/true, but every one of its call sites is rejected for null bytes before the spawn
happens, so it never executes and is unaffected. 9592 is the only fixture that actually
runs it. The other absolute-path users (9401, 9402, 9485, 9500) use /bin/sh,
/bin/bash and /bin/echo, all of which exist on macOS.
Related in spirit: #10720 (fixtures deciding a printed boolean by wall clock) and #10409
(Windows platform-specific gap failures) — same class of "the fixture, not the compiler,
is what the platform broke".
test-files/test_gap_9592_child_timeout_threads.ts:21spawns/bin/true:/bin/truedoes not exist on macOS — it lives at/usr/bin/true./bin/sleep(line 37)is present, so that arm is fine;
/bin/trueis the only offender.Effect: the ORACLE fails, not Perry
Node crashes on the unhandled
'error'event and exits 1, while Perry prints the correctanswer and exits 0:
The harness reports this as
PARITY_FAIL(output mismatch) rather thannode_fail,because Node does produce output before dying — so it reads as a Perry regression on
every macOS run. It is not in
test-parity/gap_snapshot.json, so on macOS it is anunlisted red that costs triage time. I hit it while investigating something unrelated
(#10711) and spent a while ruling it out.
Second-order finding: Perry is more lenient than Node here
Perry resolves
/bin/truewhere Node does not — presumably a PATH fallback for anabsolute path that does not exist. That is its own parity gap, independent of the fixture
path: Node's
spawnwith a non-existent absolutefilemust emitENOENTon the child's'error'event, and Perry apparently finds/usr/bin/trueinstead. Fixing only thefixture would hide that. Worth a look while the fixture is being touched, or splitting out
if it deserves its own issue.
Suggested fix
Don't hardcode absolute paths to coreutils in fixtures — the set that exists under
/bindiffers across platforms (
/bin/trueand/bin/falseare both absent on macOS;/bin/sh,/bin/echo,/bin/cat,/bin/sleep,/bin/lsare present). Either spawn by bare nameand let PATH resolve, or use
process.execPathwith-e "", which is guaranteed presentand portable.
I checked the rest of
test-files/:test_gap_9537_child_process_null_bytes.tsalso names/bin/true, but every one of its call sites is rejected for null bytes before the spawnhappens, so it never executes and is unaffected.
9592is the only fixture that actuallyruns it. The other absolute-path users (
9401,9402,9485,9500) use/bin/sh,/bin/bashand/bin/echo, all of which exist on macOS.Related in spirit: #10720 (fixtures deciding a printed boolean by wall clock) and #10409
(Windows platform-specific gap failures) — same class of "the fixture, not the compiler,
is what the platform broke".