Skip to content

test_gap_9592_child_timeout_threads spawns /bin/true, which does not exist on macOS — Node fails, Perry is correct, and the harness scores it as a Perry regression #10730

Description

@proggeramlug

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".

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions