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.
10 Comments
Additional Evidence: Bug Also Occurs on Tool Interruption
The display bug also manifests when a tool use is interrupted/declined by the user:
This shows the issue is systematic across different tool execution states (success, interruption, etc.), further confirming it's a core display rendering problem rather than an edge case.
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Not a Duplicate - Here's Why
Thank you for flagging the related issues. After thoroughly reviewing both #8990 and #8235 (including all comments), I can confirm this issue shares the same root cause but provides critical new information that warrants keeping it open:
Comparison with Referenced Issues
Issue #8990 - Very similar symptoms:
Bash(foo)"{"permissionDecision": "allow"})Issue #8235 - Related but started differently:
Why This Issue Should Remain Open
[DEBUG] Hook output does not start with {, treating as plain textCore Issue
The actual tool invocation (e.g.,
Bash(pwd)) is completely missing from the display, replaced by PreToolUse hook execution details. This makes it impossible to monitor what Claude is actually doing, which is a critical usability and transparency issue.I believe this issue provides the freshest evidence and most thorough investigation of an ongoing systemic problem with hook/tool display rendering. Please keep it open to help the team fix the root cause.
cc: @KJ7LNW (author of #8990) - you might want to check if your issue persists in v2.0.15+
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.
It's still occurring for me.
Issue happening for me still
This issue has been automatically closed due to 60 days of inactivity. If you're still experiencing this issue, please open a new issue with updated information.
This issue was closed incorrectly despite recent human comments. This behavior of the bot is reported at https://github.com/anthropics/claude-code/issues/16497. Please upvote that issue, so maybe it gets noticed.
issue is still very much active
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.