PreToolUse:Bash hooks not invoked in v2.1.176 (registered hooks silently inert)

Status Fixed / completed
Maintainer reply None cached
Activity 4 comments · opened Jun 22, 2026 · closed Aug 19, 2026

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 — not acceptEdits, not bypassPermissions)
  • Project: empty .claude/settings.local.json removed, user-level permissions: {} (empty)

Repro

  1. Register a PreToolUse hook in ~/.claude/settings.json:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash,PowerShell",
        "hooks": [
          {
            "type": "command",
            "command": "/Users/me/probe-hook.sh"
          }
        ]
      }
    ]
  }
}
  1. 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
  1. In Claude Code v2.1.176, ask Claude to run a Bash command:
please run echo SAFETY_PROBE_$(date +%s)
  1. 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

  • jq confirms 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.json deleted — no project override.
  • Session restarted with claude in 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,PowerShell form.
  • Specific code path in Bash tool invocation skips the PreToolUse chain.

Happy to provide additional traces or test against a private build.

View original on GitHub ↗

3 Comments

pascals-ager · 2 months ago

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

  • Version: claude --version2.1.191 (Claude Code)
  • Hooks configured: PreToolUse with matcher Bash(aws *)
  • Expected behavior: Block AWS CLI commands without AWS_PROFILE
  • Actual behavior: All AWS commands execute without hook invocation

Diagnostic findings

  1. Hook registration verified: jq '.hooks' ~/.claude/settings.json shows null (no user-level hooks), plugin hooks configured in ~/.claude/plugins/cache/$PATH/hooks/hooks.json
  2. Plugin enabled: jq '.enabledPlugins' ~/.claude/settings.json confirms plugin is active
  3. Hook never invoked: grep -o '"hookResult"' ~/.claude/history.jsonl | wc -l0 (zero hook invocations across entire session)
  4. Binary works when called directly: Running exact hook binary/script manually with test input returns proper deny decisions - so I know the hook logic is still successful

Impact

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:Bash hooks are registered but never invoked. The hook execution system appears completely broken for the main conversation loop (unclear if subagents are affected).

JefStat · 1 month ago

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.json lastSessionMetrics confirms it without needing a custom probe script

For any project, ~/.claude.jsonprojects["<path>"].lastSessionMetrics records hook_duration_ms_count and pre_tool_hook_duration_ms_count for the last completed session. On affected sessions, pre_tool_hook_duration_ms_count is absent entirely (not zero — the key doesn't exist), while hook_duration_ms_count is 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-declared PreToolUse/PostToolUse hook (writing one log line per tool call) and a completely unrelated PreToolUse hook 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 lastSessionMetrics JSON or hook scripts if useful for the bisect.

Dravic · 1 month ago

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:

    "PreToolUse": [
      {
        "matcher": "Grep|Glob|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "C:\\Users\\Admin\\AppData\\Roaming\\npm\\node_modules\\memtrace\\node_modules\\@memtrace\\win32-x64\\bin\\memtrace.exe route --hook"
          }
        ]
      }
    ]

Showing cached comments. Read the full discussion on GitHub ↗