Skip to content

Claude Extended Thinking: assistant message content order causes API error #9364

Description

@Soein

Bug Description

When using Claude models with Extended Thinking enabled through a proxy (e.g., via @ai-sdk/anthropic), multi-turn conversations with tool use fail with the following error:

messages.45.content.0.type: Expected 'thinking' or 'redacted_thinking', but found 'tool_use'

Root Cause

Claude Extended Thinking requires that assistant messages must start with a thinking or redacted_thinking block when thinking is enabled. However, OpenCode's message reconstruction does not guarantee this ordering.

Code Analysis

In packages/opencode/src/session/message-v2.ts, the toModelMessage function builds message parts in iteration order:

for (const part of msg.parts) {
  if (part.type === "text") /* add text */
  if (part.type === "tool") /* add tool */  
  if (part.type === "reasoning") /* add reasoning */
}

The parts are sorted by their generated ID:

result.sort((a, b) => (a.id > b.id ? 1 : -1))

In packages/opencode/src/provider/transform.ts, the normalizeMessages function for Anthropic only filters empty content but does not reorder parts to ensure reasoning blocks come first:

if (model.api.npm === "@ai-sdk/anthropic") {
  msgs = msgs
    .map((msg) => {
      // ... only filters empty text/reasoning parts
      const filtered = msg.content.filter((part) => {
        if (part.type === "text" || part.type === "reasoning") {
          return part.text !== ""
        }
        return true
      })
      // ...
    })
}

Reproduction Steps

  1. Configure a Claude model with Extended Thinking enabled (e.g., claude-sonnet-4-5 with thinking.budgetTokens > 0)
  2. Start a conversation that triggers tool use
  3. Continue the conversation for multiple turns
  4. Eventually, the API returns the error about unexpected tool_use at position 0

Proposed Fix

Add reordering logic in normalizeMessages for Anthropic models to ensure reasoning parts always come before other content types:

if (model.api.npm === "@ai-sdk/anthropic") {
  msgs = msgs.map((msg) => {
    if (msg.role === "assistant" && Array.isArray(msg.content)) {
      // Ensure reasoning blocks come first (Claude Extended Thinking requirement)
      const reasoningParts = msg.content.filter(p => p.type === "reasoning")
      const otherParts = msg.content.filter(p => p.type !== "reasoning")
      return { ...msg, content: [...reasoningParts, ...otherParts] }
    }
    return msg
  })
  // ... existing empty content filtering
}

Environment

  • OpenCode version: latest
  • Provider: Custom proxy using Anthropic-compatible API
  • Model: Claude models with Extended Thinking enabled

Related

Activity

  1. github-actions commented on Jan 19, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    The most relevant duplicate appears to be #6176, which was closed on Dec 26, 2025 but the issue persists. The underlying cause seems to be message content ordering not being enforced properly across different providers and model configurations.

    Feel free to ignore if your specific case differs from these.

  2. Soein commented on Jan 19, 2026

    @Soein
    Author

    Update: Related to #6176

    After reviewing #6176, it appears this may be the same root cause - the Antigravity adapter not correctly converting thoughtSignature to the providerMetadata.anthropic.signature format that AI SDK expects.

    My Setup

    • Plugin: opencode-antigravity-auth@1.3.0
    • Proxy: CLIProxyAPI (custom proxy)
    • Provider configured as @ai-sdk/anthropic compatible

    Possible Causes

    1. Signature format mismatch (per Claude extended thinking + tool use: signature not preserved in message history #6176): The adapter may not wrap signatures in the correct format
    2. Message ordering (my original analysis): transform.ts doesn't reorder reasoning parts to be first

    Questions

    1. Is opencode-antigravity-auth based on oh-my-opencode's Antigravity adapter?
    2. If so, does it include the fixes from oh-my-opencode PR Install on Apple M3 Max #256?

    If this is confirmed as a plugin issue rather than OpenCode core, please feel free to close as duplicate of #6176.

  3. rekram1-node commented on Jan 21, 2026

    @rekram1-node
    Collaborator

    Oh if you are using a plugin and getting an issue I highly recommend opening issue w/ them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions