Bug: interrupting a running Bash tool call does not prevent its side effect, but the rejection text says nothing was written
Interrupting (Esc) a Bash tool call that is already running does not roll back what the command already did. The harness then returns a fixed message stating the tool use "was rejected" and that nothing was written. For a command that had already completed its state change, that message is false.
Reproduction
Reproduced deliberately on 2026-08-21, after hitting it in real work on 2026-08-19.
- Create a throwaway git repo and stage a change.
- Install a
post-commithook that logs one line per second for 60 seconds. - Have Claude Code run
git commit -m "probe"as a Bash tool call. - Press Esc a few seconds after the command appears.
Result:
- The commit exists. HEAD moved off the seed commit; the staged change was consumed.
- The hook log shows it started, ran 5 ticks, trapped SIGTERM, then died without finishing.
- Claude Code reported: "The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file)."
The commit completed, and the harness reported that it had not.
Control run, same fixture, no interrupt: the hook runs all 60 ticks to completion. So the truncated log is the interrupt, not a flaky fixture.
What the signal trace shows
The hook trapped SIGTERM at the keypress, emitted two more ticks, then was killed - a terminate-then-kill escalation arriving after git had already written the commit object and updated the ref. Git's commit is atomic at the ref update; the post-commit hook is epilogue, and killing the epilogue cannot unwind the commit.
Confirmed independently of Claude Code: killing the same command with SIGKILL, SIGTERM or SIGINT during the post-commit hook always leaves the commit; killing during the pre-commit hook never does. So this is not signal-strength dependent, and it is not a git bug.
Why the message matters
The text appears to be the pre-execution rejection message reused on the post-execution interrupt path. When a user denies a command before it runs, "nothing was written" is true. When a user interrupts a command already running, the same text is emitted and can be false.
Practical impact: an operator who interrupts and reads that message reasonably concludes no state changed. In our case it left a real commit in a repo with an auto-push hook, and the branch sat holding an unpushed commit nobody knew existed. Any workflow that treats Esc as a reliable stop inherits this.
Asks
- Should the post-execution interrupt path emit different text from the pre-execution rejection path? The shared wording asserts something the harness cannot know once the command has started.
- Is there a documented guarantee for what Esc does to an in-flight Bash call? We measured SIGTERM followed by a harder kill.
- Should the interrupt result indicate that the command had already started, so an agent knows to re-verify state rather than trust the message?
Related
#85057 mentions a session continuing after an interruption but carries no reproduction and does not cover the state-change or messaging aspect. #85713 and #82329 match on the word "interrupt" but concern agent nondeterminism and session cuts. We found no existing report of an interrupted tool call completing its state change.
Environment
Claude Code 2.1.231 / macOS 26.4 arm64 / git 2.50.1