Skip to content

threads pool worker isolate SIGSEGVs in V8 GC on large suites (vite 8 rolldown, node 24/26, macOS arm64) #11291

Description

@Mearman

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::DeinitHeap::StartTearDownCppHeap::StartDetachingIsolateHeap::CollectGarbageGlobalHandles::InvokeFirstPassWeakCallbacks → SIGSEGV (worker teardown GC), or
  • IncrementalMarkingJob::Task::RunInternalMarkCompactCollector::StartMarkingMarkingWorklists::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' (stryker-mutator/stryker-js#6223) — but the repro above has no Stryker involved.

Happy to run instrumented builds or test candidate fixes against the repro if that helps.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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