PostToolUse hooks cannot recover the original tool_input after a PreToolUse hook rewrites it via updatedInput
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
- Add a PreToolUse:Bash hook that returns
hookSpecificOutput.updatedInput.command, wrapping the command in any launcher passed after--. - Add a PostToolUse:Bash hook that logs
tool_input.command. - 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-
updatedInputvalue.
Request
Any one of these resolves it:
- Add an
original_tool_inputfield to the PostToolUse payload, carrying the model's requested input before anyupdatedInputmerge. - Provide a hook ordering or priority mechanism so an observer hook can run before rewriting hooks for the same event.
- Expose the sequence of
updatedInputmutations (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.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗