Skip to content

[BUG]: Concurrent Node processes initializing PGlite can SIGSEGV the Node runtime #1053

Description

@gadicc

Describe the bug

When multiple independent Node processes initialize PGlite concurrently, an intermittent process receives a native SIGSEGV. This is reproducible without Vitest, Vite, application code, persistence, or a native addon, and still occurs when every PGlite client is explicitly closed in finally.

The current evidence suggests a Node/V8 WebAssembly-runtime interaction rather than a JavaScript exception in PGlite. I am reporting it here as well because PGlite's PostgreSQL WASM workload is the reliable trigger and PGlite maintainers may recognize a compilation or generated-WASM pattern that can help reduce or route it.

Node issue: nodejs/node#64500

Minimal repository: https://github.com/gadicc/node-pglite-wasm-sigsegv-repro

To Reproduce - include code sample(s)

Use a Linux x64 machine with at least 20 GiB available. Sixteen simultaneous clients reached an observed cgroup peak of approximately 19 GiB.

git clone https://github.com/gadicc/node-pglite-wasm-sigsegv-repro.git
cd node-pglite-wasm-sigsegv-repro
docker build -t pglite-node-sigsegv-repro .
docker run --rm pglite-node-sigsegv-repro

The Dockerfile uses the official node:26-bookworm image and the checked-in npm lockfile. Each wave launches 16 independent processes. Each child does only:

import { PGlite } from "@electric-sql/pglite";

const client = new PGlite();

try {
  await client.query("SELECT 1");
} finally {
  await client.close();
}

Two clean official-image runs failed at waves 13 and 8. The sequential control using the same workload passed 50/50 waves.

Logs

node=v26.5.0 v8=14.6.202.34-node.24 platform=linux arch=x64 children=16 waves=50
wave=1 passed=16/16
...
wave=8 passed=15/16
child=10 code=null signal=SIGSEGV elapsedMs=4315
failedWaves=1 completedWaves=8 requestedWaves=50

The failing cgroup recorded oom=0 and oom_kill=0.

Native inspection found an initial SEGV_MAPERR at a guarded WASM address. Node's node::TrapWebAssemblyOrContinue then re-raised SIGSEGV after V8 did not classify the address as a valid WASM trap. Other retained crashes entered V8 lazy WASM compilation (LiftoffAssembler::PrepareCall / Runtime_WasmCompileLazy) and v8::internal::MarkingBarrier::MarkValueLocal. Raw cores are not public because process memory can contain environment values.

Details

  • PGlite: 0.5.4
  • Extensions: none
  • OS: official Debian Bookworm Node image on Linux x64
  • Node: v26.5.0, V8 14.6.202.34-node.24
  • Also reproduced on Node 22, 24, 25, and a Node 27 V8 canary
  • Bun 1.3.11 / JavaScriptCore control: 50/50 waves passed
  • Deno 2.8.0 / V8 14.9.207.2 control: 50/50 waves passed

Forced optimizer experiments also reproduced with PGlite 0.3.15 and 0.4.1, so the underlying native crash does not appear to have been introduced by 0.5.4. This reproducer pins 0.5.4 because it triggers the failure reliably enough to investigate.

Additional context

I built the exact PGlite 0.5.4 tag (source commit 25d0a55e1f1e4c59f26d9e125150dda88a33fd00) using the documented
pnpm build:all:debug workflow. The PostgreSQL WASM build used -g, -gsource-map, and --no-wasm-opt. Its 50,321,851-byte pglite.wasm contained 1,322 DWARF compilation units and passed LLVM's DWARF verifier.

Using Node 25.2.1, a sequential control passed 5/5 waves and eight concurrent children passed 5/5 waves. With 16 concurrent children, wave 1 passed and child 9 received SIGSEGV in wave 2. After resetting the test scope's cgroup peak counter, that run peaked at 67,306,729,472 bytes (62.7 GiB), with oom=0 and oom_kill=0.

The debug build therefore retains the concurrency-dependent native crash. The remaining practical difficulty is attaching a DWARF-aware debugger to the unpredictable one of 16 independent Node children that fails. Guidance on the preferred capture workflow for that case would be useful.

Other

  • Co-authored with Codex gpt-5.6-sol, reasoning: high, with significant human input.
  • This issue was opened by me personally by hand and without any automation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions