[BUG] PreToolUse updatedInput replaces the whole tool input — sibling fields (timeout, run_in_background) silently dropped

Status Open
Reported on v2.1.251
Maintainer reply None cached
Activity 0 comments · opened Aug 30, 2026

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

  1. Register a minimal PreToolUse hook for Bash that returns a rewritten command only:
{"hookSpecificOutput": {
  "hookEventName": "PreToolUse",
  "updatedInput": {"command": "<original command> # annotated"}
}}
  1. Have the model run a long command with an explicit timeout, e.g. sleep 180 && echo done with timeout: 600000.
  2. The session transcript records the assistant tool_use with "timeout": 600000, and the hook attachment with the command-only updatedInput.
  3. 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:

  • updatedInput is 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 updatedInput drops fields, or
  • a validation warning when updatedInput omits 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

View original on GitHub ↗