fix(ai): add repair for orphaned tool calls - #9976
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
No issues found across 7 files
Architecture diagram
sequenceDiagram
participant User as User (Browser)
participant UI as Chat UI Components
participant API as Backend API Route
participant Repair as repair_incomplete_tool_call
participant Convert as convert_to_pydantic_messages
participant AI as AI Provider (Anthropic)
Note over User,AI: Normal streaming flow (first request)
User->>UI: Send message
UI->>API: POST /chat (user message)
API->>AI: Stream response
AI-->>API: tool_use (tool call)
API-->>UI: Stream tool part (input-streaming)
UI->>UI: Show spinner for tool call
User->>UI: Stop stream / abort
UI->>API: Cancel request
API->>AI: Abort stream
Note over AI: tool_use left without<br/>corresponding tool_result
Note over User,AI: Continue flow (second request) — NEW: repair
User->>UI: Send "continue"
UI->>API: POST /chat (user message + history)
API->>Convert: Convert UIMessages to pydantic messages
Convert->>Repair: NEW: repair_incomplete_tool_call(part)
Note over Repair: Detect tool part in<br/>input-streaming / input-available
alt Incomplete static tool call (ToolInputStreamingPart / ToolInputAvailablePart)
Repair->>Repair: Rewrite to ToolOutputErrorPart
Repair-->>Convert: ToolOutputErrorPart (with interrupted message)
else Incomplete dynamic tool call (DynamicToolInputStreamingPart / DynamicToolInputAvailablePart)
Repair->>Repair: Rewrite to DynamicToolOutputErrorPart
Repair-->>Convert: DynamicToolOutputErrorPart (with interrupted message)
else Terminal or non-tool part
Repair-->>Convert: unchanged part
end
Convert-->>API: Repaired pydantic messages
Note over Convert: Now every tool_use has a<br/>matching tool_result (error)
API->>AI: Send repaired conversation
AI-->>API: Accepts messages (no 400 error)
API-->>UI: New response stream
Note over UI: Frontend display — CHANGED
UI->>UI: Render tool call with isActive=false
UI->>UI: Show CircleSlashIcon + "Interrupted" label
Note over UI: Previously spinner would loop forever<br/>now shows cancelled state
Light2Dark
force-pushed
the
sham/fix-anthropic-interrupted-tool-calls
branch
from
June 24, 2026 16:13
1d95e74 to
5b51736
Compare
Light2Dark
force-pushed
the
sham/fix-anthropic-interrupted-tool-calls
branch
from
June 25, 2026 05:50
5b51736 to
4004254
Compare
mscolnick
approved these changes
Jun 25, 2026
Contributor
|
🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.11-dev28 |
williambdean
pushed a commit
to williambdean/marimo
that referenced
this pull request
Sep 6, 2026
## 📝 Summary <!-- If this PR closes any issues, list them here by number (e.g., Closes marimo-team#123). Detail the specific changes made in this pull request. Explain the problem addressed and how it was resolved. If applicable, provide before and after comparisons, screenshots, or any relevant details to help reviewers understand the changes easily. --> <img width="645" height="409" alt="image" src="https://github.com/user-attachments/assets/90f97b56-ad70-4dc8-8114-583bf48ca3cc" /> Fixes an anthropic.BadRequestError that occurs when a user stops an AI chat mid–tool-call and then continues the conversation: ``` messages.2: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_… Each `tool_use` block must have a corresponding `tool_result` block in the next message. ``` logfire trace: https://logfire-us.pydantic.dev/public-trace/b1d4c2e1-1b7d-4219-badb-fe96103941ad?spanId=6d7eaf8fabb7761f Backend fix - Adds repair_incomplete_tool_call, which rewrites any tool part still in input-streaming / input-available into the matching typed output-error part (ToolOutputErrorPart / DynamicToolOutputErrorPart) with a "Tool call was interrupted and did not complete." message, so the conversion to pydantic-ai produces a valid tool_result. - Wires it into convert_to_pydantic_messages, running after UIMessage construction Frontend UI change - The spinner used to continually spin on broken tool calls. This makes it cancelled. ## 📋 Pre-Review Checklist <!-- These checks need to be completed before a PR is reviewed --> - [ ] For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on [Discord](https://marimo.io/discord?ref=pr), or the community [discussions](https://github.com/marimo-team/marimo/discussions) (Please provide a link if applicable). - [x] Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it. - [x] Video or media evidence is provided for any visual changes (optional). <!-- PR is more likely to be merged if evidence is provided for changes made --> ## ✅ Merge Checklist - [x] I have read the [contributor guidelines](https://github.com/marimo-team/marimo/blob/main/CONTRIBUTING.md). - [ ] Documentation has been updated where applicable, including docstrings for API changes. - [x] Tests have been added for the changes made. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/marimo-team/marimo/pull/9976?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 Summary
Fixes an anthropic.BadRequestError that occurs when a user stops an AI chat mid–tool-call and then continues the conversation:
logfire trace: https://logfire-us.pydantic.dev/public-trace/b1d4c2e1-1b7d-4219-badb-fe96103941ad?spanId=6d7eaf8fabb7761f
Backend fix
Frontend UI change
📋 Pre-Review Checklist
✅ Merge Checklist