-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Description
Preflight Checklist
- I have searched existing issues and this hasn't been reported yet
- This is a single bug report (please file separate reports for different bugs)
- I am using the latest version of Claude Code
What's Wrong?
I have been trying to set up a PreToolUse hook to modify bash commands (add timeout, rewrite known bad patterns) but no matter how hard I try, I can't get it to work.
What Should Happen?
Updated Input should be respected
Error Messages/Logs
Claude's diagnosis:
Bug Report: updatedInput ignored in PreToolUse hooks
Environment
- Claude Code version: 2.0.76
- Platform: Linux (devcontainer)
Summary
PreToolUse hooks that return permissionDecision: "allow" with updatedInput have the updatedInput field completely ignored. The original tool input is executed instead of the modified input.
Blocking (exit code 2) works correctly. Only updatedInput with allow is broken.
Hook Script Output (correct)
When run manually:
$ echo '{"tool_name": "Bash", "tool_input": {"command": "echo foo"}}' | python3 /path/to/hook.py
stdout:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Rewriting command: 'echo foo' → 'echo bar'\n Reason: TEST rule",
"updatedInput": {
"command": "echo bar"
}
}
}
The hook script exits with code 0 and outputs valid JSON with updatedInput.
Claude Code Streaming Output (shows bug)
$ echo "run: echo foo" | claude -p --debug hooks --output-format stream-json --verbose 2>&1
Tool call from Claude:
{"type":"assistant","message":{"content":[{"type":"tool_use","id":"toolu_01...","name":"Bash","input":{"command":"echo foo","description":"Print 'foo' to stdout"}}]}}
Tool result (shows original command ran, not modified):
{"type":"user","message":{"content":[{"tool_use_id":"toolu_01...","type":"tool_result","content":"foo","is_error":false}]},"tool_use_result":{"stdout":"foo","stderr":"","interrupted":false,"isImage":false}}
- Expected:
"stdout":"bar"(modified command should run) - Actual:
"stdout":"foo"(original command ran,updatedInputignored)
Blocking Mode Works (for comparison)
When hook returns exit code 2 with stderr message instead of JSON with allow:
{"type":"user","message":{"content":[{"type":"tool_result","content":"Hook PreToolUse:Bash denied this tool","is_error":true,"tool_use_id":"toolu_01..."}]},"tool_use_result":"Error: Hook PreToolUse:Bash denied this tool"}
This correctly blocks the tool and Claude sees the error. So hooks ARE running, just updatedInput is ignored.
Minimal Reproduction
- Create hook script that returns
updatedInput:
#!/usr/bin/env python3
import json, sys
input_data = json.load(sys.stdin)
if input_data.get("tool_name") == "Bash":
cmd = input_data.get("tool_input", {}).get("command", "")
if cmd == "echo foo":
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {"command": "echo bar"}
}
}))
sys.exit(0)
sys.exit(0)
- Configure in settings.json:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "python3 /path/to/hook.py"}]
}]
}
}
-
Run:
echo "run: echo foo" | claude -p -
Expected: Output shows "bar"
-
Actual: Output shows "foo"
Related Issues
- #4368 - Feature request (closed as completed in v2.0.10)
- #9185 - Documentation issue noting feature exists but undocumented
- #11113 - TypeError crash with
updatedInputon Grep tool
hook script that returns updatedInput:
#!/usr/bin/env python3
import json, sys
input_data = json.load(sys.stdin)
if input_data.get("tool_name") == "Bash":
cmd = input_data.get("tool_input", {}).get("command", "")
if cmd == "echo foo":
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {"command": "echo bar"}
}
}))
sys.exit(0)
sys.exit(0)
- Configure in settings.json:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "python3 /path/to/hook.py"}]
}]
}
}
-
Run:
echo "run: echo foo" | claude -p -
Expected: Output shows "bar"
-
Actual: Output shows "foo"
Related Issues
- #4368 - Feature request (closed as completed in v2.0.10)
- #9185 - Documentation issue noting feature exists but undocumented
- #11113 - TypeError crash with
updatedInputon Grep tool
Steps to Reproduce
- Create hook script that returns
updatedInput:
#!/usr/bin/env python3
import json, sys
input_data = json.load(sys.stdin)
if input_data.get("tool_name") == "Bash":
cmd = input_data.get("tool_input", {}).get("command", "")
if cmd == "echo foo":
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {"command": "echo bar"}
}
}))
sys.exit(0)
sys.exit(0)- Configure in settings.json:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "python3 /path/to/hook.py"}]
}]
}
}-
Run:
echo "run: echo foo" | claude -p -
Expected: Output shows "bar"
-
Actual: Output shows "foo"
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
No response
Claude Code Version
2.0.76
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
No response