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

Open 💬 3 comments Opened Jun 22, 2026 by smrafiz

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 ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗