Skip to content

SIGSEGV coredumps from better-sqlite3 probe in ensure-deps.mjs on Node v24 (gap left by #228) #331

@Alexey-Goru1ev

Description

@Alexey-Goru1ev

Summary

hooks/ensure-deps.mjs reliably produces SIGSEGV coredumps on Node v24 (and periodically on Node v22) on Linux. This is despite the ABI-aware caching + child-process probe introduced to mitigate #148 / #203 / #228. The parent hook recovers via try/catch, so the tool continues functioning — but systemd-coredump captures every crash and users see a growing pile of coredumps for what looks like normal operation.

This is the same underlying SIGSEGV that #228 was about — closing #228 was premature; the mitigation does not actually eliminate the crashes, it just hides them from the parent process.

Environment

  • node -vv24.14.1 (reproducible on NVM-managed Node 24.x)
  • uname -srLinux 7.0.0-070000-generic
  • context-mode plugin cache version: 1.0.89 (from ~/.claude/plugins/cache/context-mode/context-mode/1.0.89/)
  • Claude Code, MCP stdio transport

Reproduction

  1. Install context-mode 1.0.89 into a Claude Code session running on Node v24.14.1 (Linux).
  2. Start a session — hooks/ensure-deps.mjs runs as an auto-import side effect via SessionStart / PreToolUse hooks.
  3. ensureNativeCompat() calls probeNativeInChildProcess() which spawns:
    node -e "new (require('better-sqlite3'))(':memory:').close()"
    
  4. The child process SIGSEGVs inside better-sqlite3's native addon during dlopen / first-DB instantiation.
  5. The parent execSync catches the non-zero exit and continues. From the user's perspective the hook runs fine.
  6. But systemd-coredump still captures the child's SIGSEGV:
Actual coredump (2026-04-23 14:31 +03 local, sanitized)
           PID: 268649 (MainThread)
           UID: 1000
        Signal: 11 (SEGV)
     Timestamp: Thu 2026-04-23 14:31:32 +03
  Command Line: node -e $'new (require(\'better-sqlite3\'))(\':memory:\').close()'
    Executable: /home/alexey/.nvm/versions/node/v24.14.1/bin/node
 Control Group: /user.slice/user-1000.slice/user@1000.service/app.slice/kitty-4183-0.scope
       Storage: /var/lib/systemd/coredump/core.MainThread.1000.<boot>.268649.1776943892000000.zst
    ELF object binary architecture: AMD x86-64

Stack trace of thread 268649:
#0  0x0000000000012cc0 n/a (n/a + 0x0)

Stack trace is minimal because the crash is inside a stripped native addon (better_sqlite3.node), consistent with a V8-GC-vs-native-addon .got.plt collision well-known to better-sqlite3 on newer Node versions.

Root cause

Two separate problems compound here:

1. better-sqlite3 is unsafe on Node v24 (Linux)

The native addon SIGSEGVs during lazy dlopen + first Database instantiation on Node v24.x under V8's updated garbage collector. This is the exact failure mode #228 was opened about. The bug lives in the native addon, not in this project — but using better-sqlite3 at all on Node v24 is a losing proposition on Linux.

2. The ABI-compat mitigation actively generates coredumps as a feature

probeNativeInChildProcess() is deliberately designed to invoke the very crash that better-sqlite3 has become infamous for, just to check whether it still crashes. The parent swallows the error — but on Linux the kernel writes a coredump every single time, regardless of whether the parent catches or not. So on Node v24 (where the probe always fails), every session start yields:

  • Exit code ≠ 0 inside probeNativeInChildProcess → triggers the npm rebuild better-sqlite3 fallback.
  • npm rebuild "succeeds" but the rebuilt binary also SIGSEGVs on next probe.
  • Each probe = one coredump. A user's machine fills with them.

This is what makes #228's closure premature: the mitigation is observationally equivalent to "always crash and hide the evidence from the parent" on Node v24.

Proposed fix (PR to follow)

Gate both ensureDeps() and ensureNativeCompat() on whether a built-in SQLite binding is available:

The change is additive: ~20 lines, one new function (hasModernSqlite()), two early-returns. Fully backward-compatible for older Node users. Eliminates the coredumps entirely on modern runtimes, since the probe never runs.

PR link will be posted below.

Why a new issue (and not reopening #228)

#228 was framed around adding alternative backends. That work is valuable and is still desirable for the server-side code. This issue is narrower and more immediate: even before any server-side backend swap lands, the ensure-deps.mjs probe itself needs to stop running better-sqlite3 on Node runtimes where a safe built-in alternative exists. That fix is small, self-contained in one file, and unblocks Node v24 users today.

Happy to fold this into a reopening of #228 if the maintainer prefers.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions