Skip to content

ci: disable node test runner process isolation to stop flaky failures - #28

Merged
dsherret merged 1 commit into
mainfrom
fix-flaky-node-tests
Jul 13, 2026
Merged

ci: disable node test runner process isolation to stop flaky failures#28
dsherret merged 1 commit into
mainfrom
fix-flaky-node-tests

Conversation

@dsherret

Copy link
Copy Markdown
Owner

Problem

The test:node task fails intermittently on CI (worst on macOS) with:

Error: Unable to deserialize cloned data due to invalid or unsupported version.
    at #processRawBuffer (node:internal/test_runner/runner:469:20)

Root cause

With the node test runner's default process isolation, node --test spawns the test file as a child process and parses the child's stdout pipe for v8-serialized test events (2-byte v8 header + 4-byte length + payload). Tests in this repo spawn real OS processes with stdio: "inherit", so those grandchildren write raw bytes directly into that same pipe, concurrently with the serializer. The parser tolerates raw bytes between frames (that's why the stray 5/1/2 lines normally print fine), but a write landing mid-frame corrupts the framing and fails the entire file. It's timing-dependent, hence flaky — and macOS is the worst case since pipe writes there are only atomic up to 512 bytes (PIPE_BUF), so serialized frames larger than that can be split with a grandchild's write interleaved in the gap.

Verified the mechanism with a repro: a test spawning a child that writes FF 0F-prefixed bytes to inherited stdout under node --test deterministically destroys the run. Matches upstream reports like nodejs/node#64061.

Fix

deno-test funnels every test file through a single runner file, so process isolation buys nothing here. --test-isolation=none (stable since Node 23; CI uses 24.x) runs the tests in the main process — no serialization pipe exists, and inherited stdio goes to the real terminal. Injected via NODE_OPTIONS since the deno-test bin doesn't forward extra flags.

Full suite passes locally with the flag, and the poison-byte repro passes cleanly too. Longer term, deno-test itself could pass --test-isolation=none since its single-runner design makes isolation pure overhead.

…lures

The node test runner's default process isolation spawns the test file as
a child process and parses the child's stdout pipe for v8-serialized
test events. Tests that spawn OS processes with inherited stdio make
those grandchildren write raw bytes into that same pipe, and a write
landing mid-frame corrupts the framing, failing the whole file with
"Unable to deserialize cloned data due to invalid or unsupported
version" (see nodejs/node#64061). It's
timing-dependent and worst on macOS where pipe writes are only atomic
up to 512 bytes.

deno-test funnels all test files through a single runner file, so
process isolation buys nothing. With --test-isolation=none the tests
run in the main process and there is no serialization pipe to corrupt.
@dsherret dsherret changed the title fix(ci): disable node test runner process isolation to stop flaky failures ci: disable node test runner process isolation to stop flaky failures Jul 13, 2026
@dsherret
dsherret merged commit ddf0127 into main Jul 13, 2026
8 checks passed
@dsherret
dsherret deleted the fix-flaky-node-tests branch July 13, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant