…commands
The UserPromptSubmit `session-init` hook could kill the user's Claude Code
command (e.g. `/model`) whenever the claude-mem worker was unavailable.
`recordWorkerUnreachable()` tracks consecutive worker-unreachable failures and,
once the threshold was reached, unconditionally called `emitBlockingError()`,
which does `process.exit(2)`. On a UserPromptSubmit hook, exit code 2 is Claude
Code's "block this operation" signal, so normal commands got blocked — worst
when the worker was slow to start.
Make the hook genuinely fail open: by default a tripped threshold now surfaces
a non-blocking stderr warning and the hook continues (exit 0). The blocking
behavior is preserved behind a new opt-in setting,
`CLAUDE_MEM_HOOK_FAIL_LOUD_BLOCK` (default `false`). The `hook_failed` distress
telemetry is unchanged.
Generated-By: PostHog Code
Task-Id: bdc456fa-14f5-4c2c-8b56-746f8d4c038a
Summary
The
UserPromptSubmitsession-inithook could block the user's Claude Code command (e.g./model) withUserPromptSubmit operation blocked by hookwhenever the claude-mem worker was unavailable.The per-call worker paths already fail open, but a fail-loud counter sat on top:
recordWorkerUnreachable()tracks consecutive worker-unreachable failures and, once the threshold (default 3) was reached, unconditionally calledemitBlockingError()→process.exit(2). On a UserPromptSubmit hook, exit 2 is Claude Code's "block this operation" signal, so normal commands got killed — worst when the worker was slow to start (large DB → readiness wait times out → counter climbs).This change makes the hook genuinely fail open:
CLAUDE_MEM_HOOK_FAIL_LOUD_BLOCK(defaultfalse).hook_faileddistress telemetry is unchanged — only theprocess.exit(2)default is gone.Why
Over 90 days the
session-inithook alone emitted thousands of blocking events across hundreds of users. Blocking a core interaction — the user types a command and claude-mem refuses to let it through — is exactly the papercut that drives uninstalls. Failing open with a warning is the correct default; the hard stop is now opt-in.Test plan
tests/worker-fail-open.test.tsrunsrecordWorkerUnreachable()in an isolated subprocess and asserts the actual exit code: exit 0 + non-blocking warning by default, exit 2 only whenCLAUDE_MEM_HOOK_FAIL_LOUD_BLOCK=true.tests/cli/hook-stream-discipline.test.tsfor the fail-open default.tsc --noEmitclean; hook-lifecycle, hook-io, and settings-defaults suites pass.Closes #3267
Refs #3161
Refs #2996
Created with PostHog Code from an inbox report.