Skip to content

fix(ai): add repair for orphaned tool calls - #9976

Merged
Light2Dark merged 2 commits into
mainfrom
sham/fix-anthropic-interrupted-tool-calls
Jun 25, 2026
Merged

Light2Dark merged 2 commits into
mainfrom
sham/fix-anthropic-interrupted-tool-calls

Conversation

@Light2Dark

@Light2Dark Light2Dark commented Jun 24, 2026

Copy link
Copy Markdown
Member

📝 Summary

image

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

  • For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on Discord, or the community discussions (Please provide a link if applicable).
  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

Review in cubic

@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jun 25, 2026 5:51am

Request Review

@Light2Dark Light2Dark added the bug Something isn't working label Jun 24, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Loading

Re-trigger cubic

@Light2Dark Light2Dark changed the title add repair for orphaned tool calls fix(ai): add repair for orphaned tool calls Jun 24, 2026
@Light2Dark
Light2Dark force-pushed the sham/fix-anthropic-interrupted-tool-calls branch from 1d95e74 to 5b51736 Compare June 24, 2026 16:13
@Light2Dark
Light2Dark merged commit 972242e into main Jun 25, 2026
43 checks passed
@Light2Dark
Light2Dark deleted the sham/fix-anthropic-interrupted-tool-calls branch June 25, 2026 16:21
@github-actions

Copy link
Copy Markdown
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. -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants