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
1d95e74 to
5b51736
Compare
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 |
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