#1779·codegraph

Windows: vitest pool worker killed by a V8 compiler-zone OOM, invisible in the JSON report

Author: bompusCreated Sep 8, 2026Updated Sep 16, 2026

Summary

Running the full suite on Windows kills a vitest pool worker with a V8 compiler-zone OOM. It has reproduced across separate runs with a byte-identical native stack. The crash is not visible in the JSON report — a dying worker is not a test failure, so it appears in no testResults entry and vitest recycles the worker and continues. A run can therefore report an internally consistent pass/fail summary while a worker was in fact killed.

This is a Zone OOM, not a JS heap OOM, so --max-old-space-size does not reach it.

The crash

#
# Fatal process out of memory: Zone
#
----- Native stack trace -----

 1: 00007FF6F95A495D node::MultiIsolatePlatform::DisposeIsolate+5341
 2: 00007FF6FAA12006 v8::base::FatalOOM+54
 3: 00007FF6FA29F5E5 v8::Function::NewInstance+245
 4: 00007FF6F9C19576 v8::SnapshotCreator::GetIsolate+56566
 5: 00007FF6FAE93F5A v8::platform::DelayedTaskQueue::Terminate+1698970
 6: 00007FF6FAE9364B v8::platform::DelayedTaskQueue::Terminate+1696651
 7: 00007FF6FAE92B0D v8::platform::DelayedTaskQueue::Terminate+1693773
 8: 00007FF6FAE925E4 v8::platform::DelayedTaskQueue::Terminate+1692452
 9: 00007FF6FAE91B71 v8::platform::DelayedTaskQueue::Terminate+1689777
10: 00007FF6FAE918CF v8::platform::DelayedTaskQueue::Terminate+1689103
11: 00007FF6FAC9F686 v8::base::DiyFp::Multiply+414934
12: 00007FF6FAC9E3A0 v8::base::DiyFp::Multiply+410096
13: 00007FF6FAA722EB v8::internal::compiler::CompilationDependencies::FieldTypeDependencyOffTheRecord+192907
14: 00007FF6F9912F56 DH_get0_engine+658470
15: 00007FF6F990E693 DH_get0_engine+639843
16: 00007FF6F990DF79 DH_get0_engine+638025
17: 00007FF6FAA8519F v8::platform::DefaultJobHandle::UpdatePriority+591
18: 00007FF6F95A081E node::GetNodeReport+90430
19: 00007FF6FA2F46D6 uv_thread_detach+150
20: 00007FF6FB55A033 v8::base::UnsignedDivisionByConstant<unsigned __int64>+2907267
21: 00007FF9B23DCCB7 BaseThreadInitThunk+23
22: 00007FF9B2E0AD6C RtlUserThreadStart+44

Frames 13 and 17 (internal::compiler::CompilationDependencies, platform::DefaultJobHandle::UpdatePriority) put this on a background optimizing-compiler job thread, which is why the exhausted arena is a compiler Zone rather than the JS heap.

The same stderr also carries, from tinypool:

Error: Worker exited unexpectedly
 ❯ ChildProcess.onUnexpectedExit node_modules/tinypool/dist/index.js:118:30
 ❯ ChildProcess.emit node:events:521:24
 ❯ ChildProcess._handle.onexit node:internal/child_process:295:12

Reproduction

Two full-suite runs on the same host, hours apart, on different commits, produced identical stacks. Both were vitest run --maxWorkers=8 --minWorkers=1 under Node, driven by automation that preserves each run's stderr.

The second occurrence is the more informative one. That run reported 4163 passed / 59 failed — a report with no structural sign of a crash — while a worker had been killed by this OOM. Of its 59 failures, 58 were timeouts and one was a known wall-clock assertion; none corresponded to the crash, because none could.

Why this is worth fixing separately from the timeout noise

I originally filed #1773 attributing this host's unstable failure counts to machine load. That attribution was not controlled and I have corrected it there. Relevant to this issue: the compiler-zone OOM recurred after antivirus exclusions were partially applied, so unlike the timeout class it is not plausibly a scanning artifact. It is a distinct failure mode that survived the intervention which removed the timeouts.

The reporting gap is the part I would most like addressed:

  • The JSON report cannot be used to establish that a run was healthy. I classified the second run from vitest.json alone and concluded it was clean apart from timeouts; the fatal crash was in stderr the whole time.
  • Combined with the inverse case in #1773 — a fully green report (success: true, 0 failed, 4396 passed) that still exits 1 — the exit code and the report disagree in both directions. Automation cannot currently trust either one alone.

Environment

  • Windows 11 Pro (10.0.26200), 32 logical cores, 128 GB RAM
  • Node v26.8.1 (not Bun)
  • vitest run --maxWorkers=8 --minWorkers=1, JSON + default reporters

Possible directions

I have not diagnosed the allocation that exhausts the zone, so this is a report rather than a fix, but a few things would each help independently:

  1. Surfacing worker deaths in the JSON report, or at minimum a non-zero structured signal distinguishable from ordinary test failure, so a crashed run cannot read as healthy.
  2. Whether a smaller maxWorkers or poolOptions.forks.maxForks on Windows changes reproduction — worth knowing, though note that serializing the suite did not change the timeout class on this host, so I would not assume it helps here either.
  3. Whether any test compiles or evaluates unusually large generated sources; a compiler-zone OOM points at optimizing compilation, not at retained heap.

Happy to attach the complete stderr from either run, and the JSON report from the run that looked clean.