Skip to content

@ai-sdk/anthropic: adapter produces orphaned tool_use without matching tool_result for output-error state tools #14259

@delphos-mike

Description

@delphos-mike

Description

The @ai-sdk/anthropic adapter (v3.0.67) produces Anthropic API payloads where tool_use blocks have no matching tool_result in the following message, causing the Anthropic API to reject the request with:

messages.57: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01R4gLPydCvYg7gc5G9rCkKp

Root Cause

The issue occurs when an assistant message contains a tool call in output-error state (a tool that was invoked but failed/was interrupted). The convertToModelMessages function correctly produces both tool-call and tool-result parts at the ModelMessage level — verified with 0 orphaned tool-calls across 63 model messages. However, the @ai-sdk/anthropic adapter's internal conversion from LanguageModelPrompt to the Anthropic API JSON format drops or misplaces the tool_result for the errored tool.

Reproduction

The failing message has this structure at the ModelMessage level:

  • Assistant message: [text, tool-call(output-error, id=A), text, tool-call(output-available, id=B)]
  • Tool message: [tool-result(id=A, error-text), tool-result(id=B, text)]

Both tool-calls have matching tool-results. After the adapter converts this to Anthropic API format, the API reports that tool_use id=A has no matching tool_result.

The error tool part data:

{
  "type": "tool",
  "callID": "toolu_01R4gLPydCvYg7gc5G9rCkKp",
  "tool": "edit",
  "state": {
    "status": "error",
    "input": {},
    "error": "Tool execution aborted",
    "time": { "start": 1775707868534, "end": 1775707868534 }
  }
}

This produces output-error state with errorText: "Tool execution aborted" and input: {} (empty object). The createToolModelOutput correctly returns { type: "error-text", value: "Tool execution aborted" }.

Key diagnostic evidence

  1. convertToModelMessages produces 63 ModelMessages with 0 orphaned tool-calls (verified by collecting all tool-result toolCallIds and checking every tool-call has a match)
  2. ProviderTransform.message() (OpenCode's normalization layer) preserves all 28 tool messages — none removed
  3. The Anthropic API consistently rejects at messages.57 with the same orphaned tool_use id
  4. The error reproduces on every retry because the conversation history is rebuilt identically from the database

Session-poisoning effect

Once this error occurs, the session is permanently stuck:

  1. Every turn rebuilds the same message history from the database
  2. The adapter produces the same broken API payload
  3. The API rejects with 400 (not retryable)
  4. The error is set on the NEW assistant message, not the historical one
  5. The historical message has no error flag, so it's never skipped

This is a widespread issue — see also anthropics/claude-code#6836.

Environment

  • ai: 5.0.124
  • @ai-sdk/anthropic: 3.0.67
  • API endpoint: Azure Anthropic (delphos-general-github-foundry.services.ai.azure.com)
  • Model: claude-opus-4-6

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai/providerrelated to a provider package. Must be assigned together with at least one `provider/*` labelbugSomething isn't working as documentedprovider/anthropicIssues related to the @ai-sdk/anthropic providerreproduction provided

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions