Skip to content

SessionStart sync failures are invisible: npm rebuild ... 2>/dev/null || true + --background hook hide native-binding errors from users #94

@orionsky081

Description

@orionsky081

Summary

When better-sqlite3 (or any native dep) fails to build on a user's machine, the failure is completely invisible from Claude Code's perspective. The user sees a vague "SessionStart hook error" with no indication that episodic-memory is involved, let alone which native module is broken. The plugin then silently stops archiving conversations for every future session until someone manually inspects ~/.config/superpowers/logs/episodic-memory.log.

I lost ~30 minutes to this today before tracing it to the actual error. Diagnostics paste at the bottom.

This issue is about the observability failure mode, not about fixing my specific install (npm rebuild better-sqlite3 resolved it locally). Prior install-side fixes (#56, #76/#86) won't help the next user who hits a different native-build failure — the silent-fail path will swallow that too.

Environment

  • Plugin version: 1.3.1
  • Plugin source: superpowers-marketplace
  • OS: Linux 6.6.87.2-microsoft-standard-WSL2 (Ubuntu under WSL2)
  • Node: v22.22.2
  • Claude Code: latest as of 2026-05-19

What the user sees vs what's actually happening

User-visible: Claude Code surfaces SessionStart:startup hook error. No plugin name, no stack, no path to investigate. The session otherwise appears to start normally.

Actually happening:

  1. hooks/hooks.json runs node .../cli/episodic-memory.js sync --background.
  2. Parent exits 0 immediately (because --background detaches the child). From Claude Code's view, the hook succeeded.
  3. The detached child tries require('better-sqlite3'), throws Error: Could not locate the bindings file, writes the stack to ~/.config/superpowers/logs/episodic-memory.log, and dies.
  4. Every subsequent SessionStart repeats step 1–3. Conversations stop being archived. The user has no idea.

The "SessionStart:startup hook error" Claude Code does show must be coming from somewhere else on the path (possibly a sibling hook or the non-JSON stdout Sync started in background. Log: ... line). Either way, the message points at no useful target.

Root cause of the invisibility

Two design choices compound:

  1. postinstall swallows build errors. The install path runs npm rebuild better-sqlite3 2>/dev/null || true — the 2>/dev/null || true makes a failed rebuild indistinguishable from success. npm install hangs indefinitely on macOS causing high load and massive disk I/O #41 alluded to this script's existence but the resolution there was about the macOS retry loop, not the silent-fail semantics.

  2. The SessionStart hook is fire-and-forget. sync --background detaches before doing any work that could fail. There is no preflight check that the database layer is even loadable. By the time the real error happens, the hook has already exited 0.

The combination means: a broken install reports as healthy, then a broken runtime reports as healthy, every session, forever.

What I'd love to see (not prescriptive)

Picking one or two of these would close the gap:

  1. Drop 2>/dev/null || true from the rebuild step. Build failures during install should be loud. If retry-loop concerns (npm install hangs indefinitely on macOS causing high load and massive disk I/O #41) are still live, gate retries elsewhere — not by silencing stderr.

  2. Preflight in the SessionStart hook. Before spawning the background sync, attempt a cheap require('better-sqlite3') (and the other natives). If it throws, emit hookSpecificOutput.additionalContext with the actual error and a one-liner remediation (cd <plugin-dir> && npm rebuild). Cost: one require, ~tens of ms. Benefit: every silent-fail surface I've seen reported (Node.js version of better-sqlite3 has a problem with newer node.js versions #9, Relies on better-sqlite3 which causes endless npm install when plugin runs for the first time #19, this one, likely future ones) collapses into a single visible message.

  3. Sentinel file on background failure. If preflight in the hook isn't acceptable for latency reasons, the background child could write ~/.config/superpowers/logs/last-sync-error.txt on crash, and the next SessionStart hook reads it and surfaces it via additionalContext. Same outcome, no synchronous cost on the happy path.

The framing I'd push for: failure-to-archive should never be silent. It's a passive feature; if it's broken, the user only finds out the day they try to search past conversations and discover none of the last 6 months exist.

Reproduction

Easiest way to reproduce on a clean Node install where prebuild download fails (or in WSL2 where I hit it organically):

# Pretend the binding never built
rm -rf ~/.claude/plugins/cache/superpowers-marketplace/episodic-memory/1.3.1/node_modules/better-sqlite3/build

# Start a new Claude Code session
# → Claude Code: "SessionStart:startup hook error" (no further info)
# → Log: ~/.config/superpowers/logs/episodic-memory.log shows the real error
# → No way for the user to know which plugin is responsible

Diagnostics (what I actually saw)

Error syncing: Error: Could not locate the bindings file. Tried:
 → .../node_modules/better-sqlite3/build/better_sqlite3.node
 → .../node_modules/better-sqlite3/build/Release/better_sqlite3.node
 → .../node_modules/better-sqlite3/lib/binding/node-v127-linux-x64/better_sqlite3.node
 [12 more paths]
    at bindings (.../bindings.js:126:9)
    at new Database (.../better-sqlite3/lib/database.js:48:64)
    at initDatabase (.../dist/db.js:100:16)
    at syncConversations (.../dist/sync.js:115:20)
    at async syncAll (.../dist/sync-cli.js:88:24)

Fix locally:

cd ~/.claude/plugins/cache/superpowers-marketplace/episodic-memory/1.3.1
npm rebuild better-sqlite3

Prior art I checked first

This issue is distinct from all five — they each address one install failure mode. This issue is about the observability layer that hides every install failure mode, current and future.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:hooksSession hooks and event handlingarea:syncSync and summarization pipelineenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions