[BUG] PreToolUse hooks: exit code 2 (block) shows misleading "error" prefix — add a clean "info" block mode
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?
Component: Hooks / PreToolUse
Severity: Low (UX/cosmetic)
Description:
When a PreToolUse hook exits with code 2 to block a command, Claude Code wraps the stderr output in this format:
The Error: PreToolUse:Bash hook error: prefix is misleading because:
- There is no error — the hook intentionally blocked the command to run a pre-flight check (in our case, an accessibility scan before git add/commit/push)
- Users see "Error" and think something is broken
- The long hook command path adds noise and confusion
- There's no way to suppress or customize this prefix from the hook script
Current behavior:
┌───────────┬─────────┬───────────────────────────────────────────────────────────────┐
│ Exit code │ Meaning │ Display │
├───────────┼─────────┼───────────────────────────────────────────────────────────────┤
│ 0 │ Allow │ No message shown │
├───────────┼─────────┼───────────────────────────────────────────────────────────────┤
│ 2 │ Block │ Error: PreToolUse:Bash hook error: [<full command>]: <stderr> │
└───────────┴─────────┴───────────────────────────────────────────────────────────────┘
What Should Happen?
Expected / requested behavior:
Provide a way for hooks to block a command with a clean informational message, not an error. For example:
- A new exit code (e.g., exit 3) that means "block with info" and displays stderr without the error prefix
- Or a way to set the display format via a header line in stderr (e.g., @@hook-info: <message>)
- Or a configuration option in settings.json to set displayMode: "info" | "error" per hook
Example desired output:
🔍♿ a11y scan triggered · scanning 1 component(s) before git add ...
Instead of:
Error: PreToolUse:Bash hook error:
Use case:
We built a pre-commit skill that hooks into git add, git commit, and git push via a PreToolUse hook. The hook intentionally blocks the git
command, based on logic of skill.
error condition. The "error" prefix confuses developers into thinking the hook itself is broken.
Environment:
- Claude Code CLI
- macOS / Darwin 25.4.0
- Hook type: PreToolUse → Bash matcher
Error Messages/Logs
Steps to Reproduce
- Create a PreToolUse hook in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/test-hook.sh"
}
]
}
]
}
}
- Create the hook script at .claude/hooks/test-hook.sh:
#!/usr/bin/env bash
set -euo pipefail
COMMAND="$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null || echo "")"
# Block any git add command with an informational message
if echo "$COMMAND" | grep -qE '\bgit\s+add(\s|$)'; then
echo "Running pre-stage scan before git add..." >&2
exit 2
fi
exit 0
- Make the script executable:
chmod +x .claude/hooks/test-hook.sh
- In Claude Code, ask the assistant to run:
git add some-file.txt
Actual result:
Error: PreToolUse:Bash hook error: [bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/test-hook.sh]: Running pre-stage scan before git add...
Expected result:
Running pre-stage scan before git add...
The Error: PreToolUse:Bash hook error: [bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/test-hook.sh]: prefix should not appear when the hook intentionally blocks
a command (exit 2) for informational purposes. The hook is working as designed — it is not an error condition.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.104
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗