PreToolUse:Bash hooks not invoked in v2.1.176 (registered hooks silently inert)
Summary
In Claude Code v2.1.176, PreToolUse:Bash hooks registered in ~/.claude/settings.json are not invoked when the agent calls the Bash tool. The hooks are correctly registered (visible via jq), other hook events fire normally (statusline, PostToolUse), but the PreToolUse:Bash chain is silently inert. No stdin is delivered to the hook script, no exit code is checked, no error is surfaced.
Environment
- Claude Code: v2.1.176
- Model: Opus 4.8 (1M context), Claude Max
- OS: macOS (Darwin 25.5.0)
- Permission mode:
default(verified by cycling shift+tab — notacceptEdits, notbypassPermissions) - Project: empty
.claude/settings.local.jsonremoved, user-levelpermissions: {}(empty)
Repro
- Register a PreToolUse hook in
~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash,PowerShell",
"hooks": [
{
"type": "command",
"command": "/Users/me/probe-hook.sh"
}
]
}
]
}
}
- Make the hook trivial — just log every invocation:
#!/usr/bin/env bash
printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$(cat)" >> /tmp/probe.log
exit 0
- In Claude Code v2.1.176, ask Claude to run a Bash command:
please run echo SAFETY_PROBE_$(date +%s)
- Tail the probe log:
tail -5 /tmp/probe.log
Expected: a new entry with the JSON payload Claude Code sent (tool_name, tool_input.command, cwd, etc.).
Actual: no entry. The hook was never invoked.
Other hook events in the same settings.json (statusline, PostToolUse — verified via prior log entries) fire normally. Only PreToolUse:Bash (and PreToolUse:PowerShell matchers) appear inert.
Diagnostic evidence
jqconfirms the hook IS registered:python3 -c "import json; d=json.load(open('$HOME/.claude/settings.json')); print(json.dumps([e for e in d['hooks']['PreToolUse'] if 'Bash' in e['matcher']], indent=2))"returns the expected entry.permissions: {}empty at user level — no allowlist gating to bypass.- Project-level
.claude/settings.local.jsondeleted — no project override. - Session restarted with
claudein the affected dir — same behavior. - Forensic trace file (mtime-based) shows no append on Bash tool calls in the affected session, while local CLI invocations of the same script with a piped payload DO append correctly.
Impact
Any user relying on PreToolUse:Bash for command-blocking guardrails (rm-rf protection, force-push protection, credential leak detection, etc.) has their entire safety layer silently disabled in v2.1.176. The hook is registered, the script is present and executable, but Claude Code does not call it before the Bash tool runs. Users have no in-product signal that anything is wrong.
This was discovered via a third-party safety layer (Claude Supercharger) — a unique-string probe (echo SAFETY_PROBE_<unix-timestamp> that no allowlist could match) confirmed the hook was never called for any Bash invocation in the session.
Hypotheses (untested)
- Hook chain cached at session start and the cache becomes inconsistent over time.
- Matcher parsing regression for the comma-separated
Bash,PowerShellform. - Specific code path in Bash tool invocation skips the PreToolUse chain.
Happy to provide additional traces or test against a private build.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Confirmed in v2.1.191
This bug is still present in Claude Code v2.1.191 (released 2026-06-25).
Evidence with example hook that worked until recently
claude --version→2.1.191 (Claude Code)PreToolUsewith matcherBash(aws *)Diagnostic findings
jq '.hooks' ~/.claude/settings.jsonshowsnull(no user-level hooks), plugin hooks configured in~/.claude/plugins/cache/$PATH/hooks/hooks.jsonjq '.enabledPlugins' ~/.claude/settings.jsonconfirms plugin is activegrep -o '"hookResult"' ~/.claude/history.jsonl | wc -l→ 0 (zero hook invocations across entire session)denydecisions - so I know the hook logic is still successfulImpact
The implications are severe for any user relying on PreToolUse hooks for command validation, credential protection, or trust boundary enforcement.
Confirmation
This is the same bug as v2.1.176 -
PreToolUse:Bashhooks are registered but never invoked. The hook execution system appears completely broken for the main conversation loop (unclear if subagents are affected).Reconfirmed in v2.1.204 — same symptom, new forensic method + downstream impact
Hitting this same silent-inert PreToolUse/PostToolUse behavior on v2.1.204 (macOS, Darwin 25.5.0), continuing the pattern from v2.1.176 → v2.1.191 reported above. Adding a new corroborating diagnostic and a concrete downstream-impact case.
New diagnostic:
~/.claude.jsonlastSessionMetricsconfirms it without needing a custom probe scriptFor any project,
~/.claude.json→projects["<path>"].lastSessionMetricsrecordshook_duration_ms_countandpre_tool_hook_duration_ms_countfor the last completed session. On affected sessions,pre_tool_hook_duration_ms_countis absent entirely (not zero — the key doesn't exist), whilehook_duration_ms_countis 1 or a small number (just a Session* hook, if any). On healthy sessions in other projects on the same machine, both keys are present with matching non-zero counts. This gives a quick way to confirm the regression retroactively without needing to have instrumented a probe hook in advance.Both plugin-declared AND settings.json-declared PreToolUse hooks affected together
In my repro, a plugin's
hooks/hooks.json-declaredPreToolUse/PostToolUsehook (writing one log line per tool call) and a completely unrelatedPreToolUsehook declared directly in~/.claude/settings.json(a terminal-title notifier, unrelated to the plugin) both went silent in the same session — 30+ real tool calls produced zero log entries from either, while manually invoking the exact same hook scripts with the same stdin/env worked instantly. This suggests the regression is in the shared hook-dispatch layer rather than plugin-specific hook loading (which would point away from #73952's plugin-re-materialization theory as the sole cause, at least for this case).Timing / version correlation
On this machine the CLI auto-updated 2.1.202 → 2.1.203 → 2.1.204 within about a 26-hour window. A hook-dependent background process (a PostToolUse-driven local observer) logged real activity continuously right up to that window, then nothing at all afterward across every project on the machine — consistent with the hook-dispatch break landing somewhere in that update range, matching the "still broken in 2.1.191" comment above.
Downstream impact example
A PreToolUse/PostToolUse-driven background learner (logs tool-call observations locally, periodically distills them via a headless session) silently stopped receiving any input once this hit — no crash, no error, just zero throughput, discovered only by diffing log timestamps against known CLI update dates. Same failure class as the "safety layer silently disabled" impact already noted above — anything hook-dependent (guardrails, telemetry, notifiers, background learners) fails open/silent with no user-visible signal.
Happy to provide the
lastSessionMetricsJSON or hook scripts if useful for the bisect.I'm on Claude Windows Desktop version 1.20186.1
I can't get Memtrace pre-tool-use hooks (a rail to force memtrace results to be appended when grepping etc.), either.
And mind you, I also have a hook for notifications when Claude is awaiting response, those fire just fine. But if I add the same exact notification hook for pre-tool-use (so the sounds should be going off all the time while searching) - nothing. Silence. Not working.
For context this is the memtrace hook that DOES NOT work whatsoever: