vitest-dev / vitest-dev/vitest
threads pool worker isolate SIGSEGVs in V8 GC on large suites (vite 8 rolldown, node 24/26, macOS arm64)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 17.1k
- Forks
- 2k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 94
Description
Describe the bug
Running a large test suite (454 files, ~4.9k tests) through the Node API with pool: 'threads' and maxWorkers: 1 reliably crashes the process with SIGSEGV during V8 GC on the threads-pool worker isolate. The same file set runs clean under pool: 'forks' (both via the CLI and via the Node API).
Reproduction
Against https://github.com/novus-power/hive (public), on a fresh clone with pnpm install:
// repro.mjs, run with `node repro.mjs` from the repo root
import { createVitest } from "vitest/node";
import fs from "node:fs";
process.env.NODE_ENV = "test";
process.env.VITEST = "1";
const report = JSON.parse(fs.readFileSync("related.json", "utf8")); // testResults[].name of the 454 related files, see below
const files = report.testResults.map((t) => t.name.replace(process.cwd() + "/", ""));
const ctx = await createVitest("test", {
config: "vitest.config.ts",
pool: "threads",
maxWorkers: 1,
coverage: { enabled: false },
watch: false,
bail: 1,
});
const specs = await ctx.globTestSpecifications(files);
await ctx.runTestSpecifications(specs);
await ctx.close();
related.json is just npx vitest related --run --reporter=json --outputFile=related.json core/graph/d1/src/*.ts (excluding test files) — the related set of that package's sources. We couldn't shrink it to a synthetic repro: a generated 420-file suite with real better-sqlite3 and PGlite usage per test does not crash; the real repo's module graph and test weight appear to be what pushes the worker isolate into the crashing GC window.
Expected behaviour
No SIGSEGV; or at worst the same behaviour forks pool exhibits (clean run).
Environment
- Vitest 4.1.10, vite 8.1.4 (rolldown)
- node 24.19.0 and node 26.8.1, both reproduce
- macOS 15 arm64 (also seen on GitHub Actions arm64 macOS runners)
Additional context
Crash signature from macOS DiagnosticReports, consistent across every occurrence (~15 crash reports on this machine alone, since these runs are part of our CI): faulting thread is the pool's WorkerThread, stack terminates in either
v8::internal::Isolate::Deinit→Heap::StartTearDown→CppHeap::StartDetachingIsolate→Heap::CollectGarbage→GlobalHandles::InvokeFirstPassWeakCallbacks→ SIGSEGV (worker teardown GC), orIncrementalMarkingJob::Task::RunInternal→MarkCompactCollector::StartMarking→MarkingWorklists::Local::~Local→ SIGSEGV (near-null deref, 0x89).
The crashing process hosts 18 rolldown-worker native threads (vite 8 transforms) alongside the worker isolate; only .node addons loaded are rolldown-binding.darwin-arm64.node and fsevents.node. This is why we originally hit it through StrykerJS — its vitest runner forces pool: 'threads' (https://github.com/stryker-mutator/stryker-js/issues/6223) — but the repro above has no Stryker involved.
Happy to run instrumented builds or test candidate fixes against the repro if that helps.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running repro.mjs from the hive repository with the listed Node, Vitest, and Vite versions, using related.json and vitest.config.ts as described. Compare the threads-pool run through createVitest and runTestSpecifications with the clean forks-pool behavior, then investigate the reported worker-isolate teardown and incremental-marking crash paths. Done means the reproduced suite completes without SIGSEGV and matches the forks-pool result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- devtools, testing-qa
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 38/100