PostToolUse hooks cannot recover the original tool_input after a PreToolUse hook rewrites it via updatedInput

Open 💬 1 comment Opened Jul 15, 2026 by solomonneas

Summary

When a PreToolUse hook rewrites a tool call through hookSpecificOutput.updatedInput, the PostToolUse hook for that tool receives the rewritten tool_input. There is no field carrying the original input the model requested, and hooks for the same event run in parallel with no ordering guarantee. An observer or enforcement hook at PostToolUse therefore cannot recover what the model actually asked to run once any other hook has rewritten it.

Why this matters

updatedInput is genuinely useful and I depend on it. I run a PreToolUse hook that wraps every Bash command in an output-compaction launcher, rewriting

<real command>

to

compactor wrap --source claude-code -- /bin/bash -lc '<real command>'

A separate PostToolUse hook records, for a work-loop policy, whether the model ran an approved verification command. Because PostToolUse only ever sees the wrapped form, it never matches the inner command, records nothing, and the loop can never be satisfied.

The only workarounds are both worse than the platform preserving the original input:

  • Teach every downstream hook to reverse-engineer every possible wrapper. Fragile and launcher-specific.
  • Move the recording to PreToolUse, which changes the semantics from "observed a completed run" to "observed an intent to run."

This hits any user who runs a command-rewriting PreToolUse hook alongside an observability or enforcement PostToolUse hook. Output-compaction wrappers are one common case. Anything that prefixes a launcher via updatedInput runs into it.

Reproduce

  1. Add a PreToolUse:Bash hook that returns hookSpecificOutput.updatedInput.command, wrapping the command in any launcher passed after --.
  2. Add a PostToolUse:Bash hook that logs tool_input.command.
  3. Run any Bash command. The PreToolUse hook sees the original command. The PostToolUse hook sees the wrapped one. No field on the PostToolUse payload exposes the pre-updatedInput value.

Request

Any one of these resolves it:

  1. Add an original_tool_input field to the PostToolUse payload, carrying the model's requested input before any updatedInput merge.
  2. Provide a hook ordering or priority mechanism so an observer hook can run before rewriting hooks for the same event.
  3. Expose the sequence of updatedInput mutations (which hook changed what) to later hooks.

Option 1 is the smallest and most general. It lets observer hooks stay correct without knowing which other hooks are installed.

Environment

Claude Code hooks, PreToolUse updatedInput merged into PostToolUse. Confirmed against the hooks reference note that same-event hooks run in parallel and updatedInput is merged afterward, and by logging both events: PreToolUse saw the raw command, PostToolUse saw the wrapped one.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗