Summary
Vitest 4.1.4 with coverage enabled hangs deterministically on a 255-file / 4308-test React+jsdom suite. Without coverage the suite completes cleanly. Adding coverage makes vitest's module loader stall partway through the run; tests never start, no test timeout fires, vitest never prints a summary.
I don't have a minimal reproducer (sorry — happy to put one together if maintainers indicate it would help). But I've ruled out enough configuration variables that I think the cause is narrow enough to be useful as-is.
Environment
- vitest `4.1.4`
- `@vitest/coverage-v8` `4.1.2` (also reproduced with `@vitest/coverage-istanbul` `4.1.5`)
- Node `24.x` on `ubuntu-24.04` (GitHub Actions `ubuntu-latest`)
- `jsdom` environment, React 18 + Testing Library, `@vitejs/plugin-react`
- `pool: 'forks'` (default; also reproduced with `pool: 'threads'`)
- Suite size: 255 test files, 4308 tests, ~39,800 source statements across the coverage `include` glob
Symptom
With coverage enabled, vitest streams test results normally for ~50–60 seconds, files complete, then total stdout silence. The 30s `testTimeout` never fires. After 5+ minutes the run is still hung. Last successful test result is always at the same stop point, run after run.
When the wrapping run-tests script eventually force-kills the parent, GitHub Actions' orphan-process cleanup terminates 2 surviving `MainThread` workers (default 4 forks were spawned — 2 are missing, but no V8 "heap out of memory" stack trace appears anywhere in the log).
The 36 of 255 test files that complete are almost entirely pure-TypeScript: services, hooks, engine, store, utils, integration. The handful of completing component tests are the ones that mock all their imports. The moment a test file with real React component imports hits a fork, that fork goes silent.
What was ruled out
| Change |
Result |
| `NODE_OPTIONS=--max-old-space-size=6144` |
Same hang at same stop point. No V8 OOM ever logged. |
| Switch to `@vitest/coverage-istanbul` |
Same hang at same stop point. |
| `pool: 'threads'` instead of `forks` |
Same hang at same stop point. |
| Scope to `tests/components` only |
Hangs after 3 files (deterministically the 3 that mock everything). |
| `--isolate=false` |
Hang disappears. Vitest completes in seconds, runs all 256 files. (200+ failures from state pollution because suite isn't designed for shared context, and the coverage report still doesn't write before the wrapping process exits — but the loader hang is clearly gone.) |
Hypothesis
The deadlock lives in the per-file isolated module-load + instrumentation path. Each test file in default isolation gets a fresh execution context; coverage instrumentation has to be applied to every imported source file in that context. For test files that pull in deep React+jsdom+component import graphs across many forks/threads doing the same work in parallel, something deadlocks.
Notable signals:
- The hang is in module load, not test execution (no test timeout fires).
- It's independent of pool architecture (forks vs threads).
- It's independent of coverage provider (v8 vs istanbul).
- It only manifests with coverage enabled.
- It only manifests with isolation enabled (default).
- Workaround: `--isolate=false` (with the obvious cost of breaking tests that rely on isolation).
What would help me help you
- Should I produce a minimal reproducer? I can try, though shrinking a 4k-test React suite to a public repro is non-trivial. If a maintainer can suggest specific instrumentation to add (debug log points, vitest-internal flags, etc.) before I trim, that would save time.
- Are there known related upstream issues I should link?
- Any flags I haven't tried that might narrow it further?
Will keep watching the issue. Thanks for the work on vitest.
Summary
Vitest 4.1.4 with coverage enabled hangs deterministically on a 255-file / 4308-test React+jsdom suite. Without coverage the suite completes cleanly. Adding coverage makes vitest's module loader stall partway through the run; tests never start, no test timeout fires, vitest never prints a summary.
I don't have a minimal reproducer (sorry — happy to put one together if maintainers indicate it would help). But I've ruled out enough configuration variables that I think the cause is narrow enough to be useful as-is.
Environment
Symptom
With coverage enabled, vitest streams test results normally for ~50–60 seconds, files complete, then total stdout silence. The 30s `testTimeout` never fires. After 5+ minutes the run is still hung. Last successful test result is always at the same stop point, run after run.
When the wrapping run-tests script eventually force-kills the parent, GitHub Actions' orphan-process cleanup terminates 2 surviving `MainThread` workers (default 4 forks were spawned — 2 are missing, but no V8 "heap out of memory" stack trace appears anywhere in the log).
The 36 of 255 test files that complete are almost entirely pure-TypeScript: services, hooks, engine, store, utils, integration. The handful of completing component tests are the ones that mock all their imports. The moment a test file with real React component imports hits a fork, that fork goes silent.
What was ruled out
Hypothesis
The deadlock lives in the per-file isolated module-load + instrumentation path. Each test file in default isolation gets a fresh execution context; coverage instrumentation has to be applied to every imported source file in that context. For test files that pull in deep React+jsdom+component import graphs across many forks/threads doing the same work in parallel, something deadlocks.
Notable signals:
What would help me help you
Will keep watching the issue. Thanks for the work on vitest.