PreToolUse hook displayed instead of actual tool invocation
Bug Description
When a PreToolUse hook is configured, Claude Code displays the hook execution in the UI instead of (or in addition to) the actual tool invocation. The actual tool (e.g., Bash(pwd)) is not shown to the user at all, only the PreToolUse hook command appears.
This bug occurs in normal Claude Code sessions, not just in debug mode. Debug mode was used here to gather logs for investigation.
Environment
- Claude Code Version: v2.0.15
- Model: Sonnet 4.5
- OS: macOS (Darwin 24.6.0)
Steps to Reproduce
- Configure a PreToolUse hook in
.claude/settings.local.json:
{
"hooks": [
{
"trigger": "PreToolUse:Bash",
"command": "jq -r '\"\(.tool_input.command) - \(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
}
]
}
-
Run Claude Code (with or without debug mode):
claude -
Execute any bash command, e.g., "Run the pwd command"
Expected Behavior
The UI should display something like:
⏺ I'll run the pwd command to show the current working directory.
Bash(pwd) completed successfully
⎿ /Users/maximelas/Projects/project-name
⏺ The current working directory is:
/Users/maximelas/Projects/project-name
Note: I'm not 100% certain whether PreToolUse hooks are meant to be displayed to the user (and if so, in what format), but regardless, the current behavior is clearly incorrect since the actual tool invocation is completely missing.
Actual Behavior
The UI displays:
⏺ I'll run the pwd command to show the current working directory.
PreToolUse:Bash [jq -r '"\(.tool_input.command) -
\(.tool_input.description // "No description")"' >>
~/.claude/bash-command-log.txt] completed successfully
⎿ /Users/maximelas/Projects/project-name
PreToolUse:Bash [jq -r '"\(.tool_input.command) -
\(.tool_input.description // "No description")"' >>
~/.claude/bash-command-log.txt] completed successfully
⏺ The current working directory is:
/Users/maximelas/Projects/project-name
Issues:
- The PreToolUse hook command is displayed prominently to the user
- The hook appears twice
- The actual
Bash(pwd)tool invocation is completely missing from the display
Debug Log Evidence
From ~/.claude/debug/latest (collected by running claude --debug):
[DEBUG] executePreToolHooks called for tool: Bash
[DEBUG] Executing hooks for PreToolUse:Bash
[DEBUG] Getting matching hook commands for PreToolUse with query: Bash
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 unique hooks for query "Bash" (1 before deduplication)
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: jq -r '"\(.tool_input.command) - \(.tool_input.description // "No description")"' >> ~/.claude/bash-command-log.txt with timeout 60000ms
[DEBUG] Hook command completed with status 0: jq -r '"\(.tool_input.command) - \(.tool_input.description // "No description")"' >> ~/.claude/bash-command-log.txt
[DEBUG] Hook output does not start with {, treating as plain text
[DEBUG] Executing hooks for PostToolUse:Bash
Note: The hook itself is working correctly - it successfully logs to ~/.claude/bash-command-log.txt with the correct entry: pwd - Print current working directory. The issue is purely a UI display bug.
Additional Context
The line [DEBUG] Hook output does not start with {, treating as plain text suggests that Claude Code may be incorrectly treating the hook's execution result as user-visible output. The core issue is that the actual tool invocation (Bash(pwd)) is completely missing from the UI display.