PreToolUse hooks on the `Bash` matcher silently and persistently stop firing partway through a session
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Summary
With two PreToolUse hooks registered on overlapping Bash matchers in settings.json, both hooks fire correctly for the first several matching Bash tool calls in a session, then — without any error, warning, or apparent trigger — stop being invoked at all for the remainder of the session. Once it starts, the failure is persistent (every subsequent matching call is affected, not just an intermittent one), and does not recover.
Observed behavior
In a single session:
- The first several
git diff <args>invocations correctly triggered the first hook's rewrite (confirmed via the tool's own distinctive output). - Partway through the same session, with no configuration change and no apparent trigger,
git diffinvocations began running as completely unmodified, raw git — neither hook's effect was applied. - This was confirmed to not be a hook-script problem: piping the exact same
tool_input.commandJSON directly into the first hook script (echo '{"tool_input":{"command":"..."}}' | python3 ~/.claude/hooks/sem-git-rewrite) at the same moment the live call was misbehaving always returned the correct rewrite. The script's logic was correct and deterministic throughout; only the harness's live invocation of it (or dispatch to it) stopped taking effect. - Once the failure began, it did not self-recover for the rest of the session — verified across multiple different files and slightly different command phrasings.
- The second hook (a separate tool's own Bash-output post-processing, unrelated to the first hook's logic) stopped taking effect at the same point too, suggesting the fault is in hook dispatch for the matcher generally, not specific to either hook's own code.
What Should Happen?
PreToolUse hooks registered on a matcher should be invoked consistently for every matching tool call for the duration of a session, or any internal failure to do so should surface as a visible error/warning rather than silently degrading to "hook did not run."
Error Messages/Logs
Steps to Reproduce
Hook configuration
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/sem-git-rewrite",
"if": "Bash(git *)",
"statusMessage": "Rewriting to sem..."
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "rtk hook claude"
}
]
}
]
Both hooks are registered on the Bash matcher (the first gated further by an if: "Bash(git *)" condition). The first hook rewrites git diff/git blame commands to an alternate tool via updatedInput.command in its JSON output; the second does its own separate rewriting/reformatting of Bash output for token savings.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.205 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
Environment
- Two
PreToolUsehooks on theBashmatcher (one further scoped by anifcondition), as shown above. - OS: Linux (WSL2).
- Happened during a long, tool-call-heavy session involving many
gitoperations (commits, diffs across a 12-task implementation plan, a rebase, a merge).
Additional context
This was found while debugging what first looked like a bug in one hook script's own logic (a git diff argument-rewriting bug). After fixing and thoroughly verifying that script in isolation (confirmed correct via direct invocation, every time, including during the live failure), the remaining discrepancy pointed at hook dispatch itself rather than either hook's implementation. Happy to provide the hook script for sem if useful for reproduction — the scripts are a personal customization, not part of Claude Code itself, but the dispatch behavior clearly is. The second hook is implemented by the rtk binary.