[BUG] PreToolUse updatedInput replaces the whole tool input — sibling fields (timeout, run_in_background) silently dropped
Summary
A PreToolUse hook's hookSpecificOutput.updatedInput is applied as a replacement for the entire tool input, not a patch. A hook that rewrites only command (the natural way to write a command-rewriting hook) silently drops every sibling field the model passed — timeout, run_in_background, description. The model's explicitly requested timeout: 600000 is discarded and the command is killed at the 120s default (Command timed out after 2m 0s, exit 143), with no indication to the user or the model that the timeout was lost.
Environment
- Claude Code 2.1.251, Windows 11 (win32 x64), Git Bash
- Also reproduced on 2.1.248
Repro
- Register a minimal PreToolUse hook for Bash that returns a rewritten command only:
{"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"updatedInput": {"command": "<original command> # annotated"}
}}
- Have the model run a long command with an explicit timeout, e.g.
sleep 180 && echo donewithtimeout: 600000. - The session transcript records the assistant
tool_usewith"timeout": 600000, and the hook attachment with the command-onlyupdatedInput. - The command is SIGTERMed at 120s:
Command timed out after 2m 0s, exit 143.
Control: if the hook copies the full tool_input and overrides only command, the timeout is honored.
Expected
One of:
updatedInputis merged over the original tool input (sibling fields preserved unless the hook explicitly sets them), or- the replacement semantics are prominently documented in the hooks docs with a warning that partial
updatedInputdrops fields, or - a validation warning when
updatedInputomits fields present in the original input.
Actual
Silent whole-input replacement. The failure is invisible: the transcript shows the timeout was requested, the command dies at the default, and nothing connects the two. We traced it only via the binary string "updatedInput is missing or empty, falling back to original tool input" (i.e., when present it is used whole) plus A/B testing through a proxy that logs hook I/O.
Impact
Any command-rewriting PreToolUse hook (path rewriting, sandbox wrapping, exit-code instrumentation) breaks long-running commands in a way that looks like a flaky 2-minute timeout. Related but distinct: #77851 (PostToolUse can't see original input after rewrite), #83353 (multi-hook rewrite race).
🤖 Generated with Claude Code