Skip to content

Coverage deadlocks in per-file isolation on large React+jsdom suite (4308 tests / 255 files) #10252

@popculturepm

Description

@popculturepm

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions