Repository navigation
Description
Description
Bug: Interrupted tool calls permanently corrupt session history
Problem
When a tool call is interrupted (network timeout, proxy error, abort), OpenCode records the tool_use block in the assistant message but does not insert a corresponding tool_result block (even a synthetic error one). This makes the session permanently broken -- every subsequent message attempt is rejected by the Claude API with:
messages.N: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_bdrk_XXXX
The session cannot recover without manual intervention (revert/undo).
Compounding issue: empty error in TUI
When this validation error occurs, session.error fires with an empty error field, and the TUI shows zero feedback. The user sees a silent hang with no indication of what went wrong.
Root Cause
The Go core does not ensure message history integrity when tool execution is interrupted. The fix should be: always insert a tool_result with error status when a tool_use does not complete, regardless of the interruption cause.
Evidence
Session errors occurring 1-4ms after user message (pre-request validation, not network timeout):
11:04:35.902 user_message
11:04:35.906 session_error error:"" (4ms -- request never left the client)
Full Bedrock error when error IS surfaced:
InvokeModelWithResponseStream: operation error Bedrock Runtime,
StatusCode: 400, ValidationException:
messages.53: tool_use ids were found without tool_result blocks
immediately after: toolu_bdrk_012CSgTxpSpwK877z7LK5M2Y
This affects both old sessions AND newly created sessions (corruption happens actively).
Expected Behavior
- When a tool call is interrupted, insert a synthetic
tool_resultwith error status to maintain message format compliance session.errorshould include the actual error message, not an empty string- TUI should show visible feedback on any session error
Workaround
Plugin that hooks session.error, scans for orphaned tool_use blocks, and attempts session.revert().
Environment
- OpenCode: latest
- OS: Windows 11
- Provider: API proxy -> AWS Bedrock -> Claude Sonnet 4 / Opus 4
- Frequency: Multiple times per day
Plugins
No response
OpenCode version
No response
Steps to reproduce
No response
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response
Activity
github-actions commentedon Apr 7, 2026
This issue might be a duplicate of existing issues. Please check:
tool_useids were found withouttool_resultblocks immediately #10616: Same errortool_use ids were found without tool_result blocks immediately aftertool_useids were found withouttool_resultblocks immediately after #1662: Sametool_use ids were found without tool_result blocks immediately aftererrorIf your issue is distinct or adds new context not covered by the above, please clarify the differences so it can be tracked separately.
bvironn commentedon Apr 11, 2026
This affects me as well — v1.4.3, WSL2/Linux, Claude Opus 4.6 via Anthropic API. Sessions become permanently unrecoverable once hit.
Regarding the bot's duplicate flag: this is the same symptom as #1662 and #10616, but the root cause has multiple entry points. The partial fixes applied so far address individual vectors but miss the systemic issue. Adding source-level analysis below.
Healing logic exists but is insufficient
There IS healing logic in
message-v2.ts(lines 779-790) that catches pending/running tool calls and converts them tooutput-errorparts:And
processor.ts(lines 489-512) marks interrupted tool calls asstatus: "error"withmetadata: { interrupted: true }during cleanup. All fourToolStatevalues (pending, running, completed, error) are handled intoModelMessages.Despite this, the bug persists because three code paths bypass the healing entirely.
Vector 1 —
normalizeMessagesintransform.ts(lines 56-74)For Anthropic/Bedrock providers, messages with empty content after filtering get dropped:
This runs AFTER
convertToModelMessageshas already separatedtool_useandtool_resultinto distinctModelMessageentries. If a message in theModelMessage[]chain gets eliminated here, it breaks thetool_use→tool_resultpairing that the Anthropic API requires.Vector 2 — Error-skip in
message-v2.ts(lines 697-705)When a previous assistant message failed with an
APIError(including this exacttool_use without tool_resulterror), the message is skipped entirely on the next attempt. But its tool parts may have already influenced theModelMessage[]chain viaconvertToModelMessages. The skip removes the message but not its downstream effects.Vector 3 — Lost step boundaries during retries (#16749)
When the
finish-stephandler throws during a retryable error,step-finishfor step N andstep-startfor step N+1 are never persisted. Both steps' content merges into a single DB message. On replay,convertToModelMessagesproduces interleavedtool_use/text/reasoningcontent in a single assistant block — which Anthropic rejects. #16749 documents this in detail with SQL evidence from a real session.The retry policy doesn't help
In
retry.ts,retryable()returnsundefinedfor non-retryable errors. Anthropic's 400 Bad Request (isRetryable: false) means the corrupted request fails permanently on first attempt — no retry, no recovery.This is a long-standing systemic issue
This has been open since #1662 (February 2026). Related issues include #8377, #10616, #16749, #17065, #14367, #21489, #2720, #5750, #2214, #8312, #8010. Partial fixes have been applied to individual vectors, but the core problem persists because each fix targets one entry point while the others remain open.
Suggested fix: post-transform validation at the
ModelMessage[]levelThe healing logic in
toModelMessagesand the cleanup inprocessor.tswork at the part level — before the data passes throughconvertToModelMessagesandnormalizeMessages. All three vectors corrupt the data after that point.The fix that catches all three vectors is a validation pass on the final
ModelMessage[]array, after bothconvertToModelMessagesandnormalizeMessageshave run. Pseudocode:This is the only point in the pipeline that sits downstream of all three corruption vectors. Fixing individual vectors is still worthwhile, but without this safety net, new edge cases will keep producing the same symptom.
rekram1-node commentedon Apr 16, 2026
fix: 348a849
phazei commentedon May 5, 2026
Totally not fixed