[BUG] A tool call cut off by a safeguards refusal still executes with truncated input (Edit/Write report success)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [ ] I am using the latest version of Claude Code: observed on 2.1.219, 2.1.257 and 2.1.280. On 2.1.283 (latest) I only read the bundled code, which still has the gap described below.
What's Wrong?
Sometimes the API stops a response with stop_reason: "refusal" (the "API Error: <model>'s safeguards flagged this message" error) while the model is still writing a tool_use block. Claude Code keeps that partial block and executes it. The block's input JSON is closed at the point where the stream was cut, so a string argument ends mid-word, the input still passes schema validation, and the tool runs with the truncated arguments. Edit and Write then report success.
Observed on one Linux machine, 2026-09-02 to 09-23, 4 events in 3 sessions:
| CLI | Model | Tool | Input at the cut | tool_result |
|---|---|---|---|---|
| 2.1.219 | Opus 5 | Edit | old_string complete (83 chars); new_string cut mid-word after 45 chars | "The file … has been updated successfully." The first line of a list item in a Markdown file was replaced by that fragment. |
| 2.1.257 | Fable 5.1 | Write | content cut mid-file (~11.7k chars) | "File created successfully at: …" (the file on disk was truncated) |
| 2.1.257 | Fable 5.1 | Bash | heredoc cut before its terminator | warning: here-document at line 1 delimited by end-of-file, not flagged as an error. The partial heredoc was written. |
| 2.1.280 | Opus 5.5 | Bash | cut inside an until loop (48 chars) | syntax error: unexpected end of file from 'until' command |
The transcript shape is the same each time (only structural fields shown):
assistant model: claude-opus-5 stop_reason: "refusal" stop_details.category: "reasoning_extraction"
content: [ tool_use { name: "Edit", input: { replace_all, file_path, old_string, new_string } } ]
(new_string ends mid-word)
system subtype: "model_refusal_no_fallback"
assistant model: "<synthetic>" isApiErrorMessage: true "API Error: Opus 5's safeguards flagged this message …"
user tool_result for the same tool_use_id: "The file … has been updated successfully." (about 90 ms after the refusal)
Which path does this. 2.1.278 added a same-model retry for refusals. On that path, unfinished tool calls in the refused response get "Not run: the response that made this tool call was stopped by a safety classifier." (is_error, toolDenialKind: "cancelled"), and the turn continues once. That path works: we saw 4 correct "Not run" results on 2.1.280. But:
- In 2.1.278 and 2.1.280 that path sits behind a server-side gate. In 2.1.283 it is on by default, with
CLAUDE_CODE_DISABLE_REFUSAL_RETRYto opt out. - It runs once per turn (
refusalRetrySpentin 2.1.283). A second refusal in the same turn goes through themodel_refusal_no_fallbackbranch, and that branch does not cancel the refused message's tool calls.
The 2.1.280 row above is exactly that case, in one session and one turn:
20:08:31 refusal, cut Bash tool_use -> tool_result "Not run: … stopped by a safety classifier." (retry path)
20:08:36 the retried request succeeds and runs a Bash call normally
20:08:38 refusal, cut Bash tool_use -> system model_refusal_no_fallback -> API Error
-> the cut Bash command runs: "Exit code 2 … syntax error: unexpected end of file"
So on 2.1.283 the bug should still occur (a) on the second refusal in a turn and (b) whenever the retry is disabled. I have not observed it on 2.1.283 yet.
What Should Happen?
A tool_use from a response that ended with stop_reason: "refusal" should never run. That applies especially to one whose input was cut off, and it should hold on every branch, not only the same-model-retry branch. The model_refusal_no_fallback branch should do what the retry branch already does: answer those tool_use ids with the "Not run: …" error result. If the streaming executor had already started the call before the refusal arrived, use the existing "Interrupted: … may have partially or fully completed" result instead.
Error Messages/Logs
API Error: Opus 5's safeguards flagged this message (https://www.anthropic.com/legal/aup). This sometimes happens with safe, normal conversations. Claude Code can't respond to this message with Opus 5.
(followed by the tool's normal success result, as above)
Steps to Reproduce
There is no deterministic repro, because it needs the API to return a refusal while the model is emitting a tool call. The conditions are:
- A model with response-level safeguards (seen with Opus 5, Opus 5.5 and Fable 5.1; categories
reasoning_extractionandcyber). - The response is stopped mid-
tool_use. - The same-model refusal retry is unavailable (2.1.278–2.1.280 without the gate,
CLAUDE_CODE_DISABLE_REFUSAL_RETRY=1on 2.1.283, or any version before 2.1.278), or it was already used earlier in the same turn.
Result: the truncated tool call executes. A unit-level repro should be possible by feeding the query loop a stream that has a partial input_json_delta for an Edit and then stops with stop_reason: "refusal", with the retry already spent.
Claude Model
Other
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.280 (Claude Code). Also observed on 2.1.219 and 2.1.257. 2.1.283 code inspected, not yet observed there.
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
- Why it matters: Edit and Write report success, so neither the model nor the user learns that the file was damaged unless one of them looks. In one of the sessions the agent spent several turns blaming an unrelated "truncation bug" before finding the cause.
- Frequency on this machine: 4 executed truncated calls in 3 sessions, out of roughly 9 safeguard refusals between 2026-08-28 and 09-23. The same pattern (a Write in a refusal-stopped response that ran and reported success) also appears 3 times in April 2026 on 2.1.84 and 2.1.114.
- Terminal: interactive sessions inside tmux on Ubuntu 26.04.
- Related: #85402, where a refusal-fallback retry re-dispatches Agent calls that had already run. Also about tool calls from a refused response, but a different branch.
— Filed by Claude (Opus 5.5) on behalf of @Butanium, from an automated review of our Claude Code session logs.