Empty message content causes Anthropic API errors during compaction #3998

Closed
opened 2026-02-16 17:42:14 -05:00 by yindo · 7 comments
Owner

Originally created by @Melodeiro on GitHub (Dec 30, 2025).

Originally assigned to: @rekram1-node on GitHub.

Description

During compaction, Anthropic API returns:

messages.9: all messages must have non-empty content except for the optional final assistant message

Root Cause Analysis

After investigating the storage data, found the following message structure in the problematic session:

[0] user     | parts: text
[1] assistant | parts: step-start, tool (completed), step-finish  ← NO TEXT
[2] assistant | parts: step-start, tool (completed), step-finish  ← NO TEXT, consecutive
[3] assistant | parts: step-start, tool (completed), step-finish  ← NO TEXT, consecutive
[4] assistant | parts: step-start, text, step-finish
[5] user     | parts: text
...

Key observations:

  1. Messages [1], [2], [3] are consecutive assistant messages with only tool calls (no text)
  2. step-finish part type is not handled in toModelMessage() (lines 480-537)
  3. The filter on line 541 passes these messages because they have tool-* parts (not just step-start)

The Conversion Pipeline

In packages/opencode/src/session/message-v2.ts:

// Line 541
return convertToModelMessages(result.filter((msg) => msg.parts.some((part) => part.type !== "step-start")))

The convertToModelMessages from AI SDK (ai package) then converts UIMessage[] to ModelMessage[].

Hypothesis: When convertToModelMessages processes consecutive assistant messages with tool calls, it may:

  1. Create synthetic user messages for tool results
  2. These synthetic messages might end up empty in certain edge cases
  3. Or the merging logic creates an empty content array

Affected Code

  • packages/opencode/src/session/message-v2.ts:421-542toModelMessage() function
  • Called from packages/opencode/src/session/compaction.ts

Questions

  1. How does convertToModelMessages handle consecutive assistant messages with tool calls?
  2. Should toModelMessage() merge consecutive assistant messages before passing to convertToModelMessages?
  3. Should there be validation after convertToModelMessages to filter out empty messages?

Reproduction

Session with multiple consecutive assistant messages containing only tool calls (no text parts) followed by compaction trigger.

OpenCode version

1.0.209

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

Xubuntu 22.04

Terminal

ghostty

Originally created by @Melodeiro on GitHub (Dec 30, 2025). Originally assigned to: @rekram1-node on GitHub. ### Description During compaction, Anthropic API returns: ``` messages.9: all messages must have non-empty content except for the optional final assistant message ``` ### Root Cause Analysis After investigating the storage data, found the following message structure in the problematic session: ``` [0] user | parts: text [1] assistant | parts: step-start, tool (completed), step-finish ← NO TEXT [2] assistant | parts: step-start, tool (completed), step-finish ← NO TEXT, consecutive [3] assistant | parts: step-start, tool (completed), step-finish ← NO TEXT, consecutive [4] assistant | parts: step-start, text, step-finish [5] user | parts: text ... ``` **Key observations:** 1. Messages [1], [2], [3] are **consecutive assistant messages** with only tool calls (no text) 2. `step-finish` part type is **not handled** in `toModelMessage()` (lines 480-537) 3. The filter on line 541 passes these messages because they have `tool-*` parts (not just `step-start`) ### The Conversion Pipeline In `packages/opencode/src/session/message-v2.ts`: ```typescript // Line 541 return convertToModelMessages(result.filter((msg) => msg.parts.some((part) => part.type !== "step-start"))) ``` The `convertToModelMessages` from AI SDK (`ai` package) then converts `UIMessage[]` to `ModelMessage[]`. **Hypothesis:** When `convertToModelMessages` processes consecutive assistant messages with tool calls, it may: 1. Create synthetic user messages for tool results 2. These synthetic messages might end up empty in certain edge cases 3. Or the merging logic creates an empty content array ### Affected Code - `packages/opencode/src/session/message-v2.ts:421-542` — `toModelMessage()` function - Called from `packages/opencode/src/session/compaction.ts` ### Questions 1. How does `convertToModelMessages` handle consecutive assistant messages with tool calls? 2. Should `toModelMessage()` merge consecutive assistant messages before passing to `convertToModelMessages`? 3. Should there be validation after `convertToModelMessages` to filter out empty messages? ### Reproduction Session with multiple consecutive assistant messages containing only tool calls (no text parts) followed by compaction trigger. ### OpenCode version 1.0.209 ### Steps to reproduce _No response_ ### Screenshot and/or share link _No response_ ### Operating System Xubuntu 22.04 ### Terminal ghostty
yindo added the bug label 2026-02-16 17:42:14 -05:00
yindo closed this issue 2026-02-16 17:42:14 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Dec 30, 2025):

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

  • #5028: Call Missing content in messages: text content blocks must be non-empty - similar API error about empty message content during message processing
  • #5187: Ollama: User message content arrives as empty array - model cannot see user input - related issue with empty message content in the message pipeline
  • #6068: [BUG]: Auto compaction not triggering reliably with Anthropic models - compaction-related issue with Anthropic API errors

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Dec 30, 2025): This issue might be a duplicate of existing issues. Please check: - #5028: Call Missing content in messages: text content blocks must be non-empty - similar API error about empty message content during message processing - #5187: Ollama: User message content arrives as empty array - model cannot see user input - related issue with empty message content in the message pipeline - #6068: [BUG]: Auto compaction not triggering reliably with Anthropic models - compaction-related issue with Anthropic API errors Feel free to ignore if none of these address your specific case.
Author
Owner

@rekram1-node commented on GitHub (Dec 30, 2025):

Can u share a session u got this error w/?

Simply run this:

opencode export > session.json

And send the session

@rekram1-node commented on GitHub (Dec 30, 2025): Can u share a session u got this error w/? Simply run this: `opencode export > session.json` And send the session
Author
Owner

@zitongcharliedeng commented on GitHub (Dec 31, 2025):

+1 I've had to stop using Opus in opencode because of this :(

@zitongcharliedeng commented on GitHub (Dec 31, 2025): +1 I've had to stop using Opus in opencode because of this :(
Author
Owner

@rekram1-node commented on GitHub (Dec 31, 2025):

Can u plz send a session :)

@rekram1-node commented on GitHub (Dec 31, 2025): Can u plz send a session :)
Author
Owner

@zitongcharliedeng commented on GitHub (Dec 31, 2025):

Can't really since I was working on something personal, but I was around
137 messages in, latest Opencode version as of today, using the
oh-my-opencode plugin, when it suddenly just came up with this error
pattern, no matter how many undos I tried. I reinstalled and on a new
session it is somehow is fixed for now? 🤷

@zitongcharliedeng commented on GitHub (Dec 31, 2025): Can't really since I was working on something personal, but I was around 137 messages in, latest Opencode version as of today, using the oh-my-opencode plugin, when it suddenly just came up with this error pattern, no matter how many undos I tried. I reinstalled and on a new session it is somehow is fixed for now? :shrug:
Author
Owner

@rekram1-node commented on GitHub (Jan 6, 2026):

This should be fixed

@rekram1-node commented on GitHub (Jan 6, 2026): This should be fixed
Author
Owner

@BouquetAntoine commented on GitHub (Jan 9, 2026):

Hi, i don't know if this is deployed or not but got the same error on last (1.1.8) version, session not shareable as used under WSL and "copied to clipboard" = no copy. I'm using Azure Inference for Anthropic "azure-anthropic": { "name": "Azure AI Foundry (Anthropic)", "npm": "@ai-sdk/anthropic", "options": { "baseURL": "https://XXXXXXXXXXXXXX-resource.services.ai.azure.com/anthropic/v1", "headers": { "anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,token-efficient-tools-2025-02-19,context-1m-2025-08-07,context-management-2025-06-27,tool-search-tool-2025-10-19,tool-examples-2025-10-29" } }, "models": { "claude-opus-4-5-20251101": { "id": "claude-opus-4-5-20251101", "name": "Claude Opus 4.5 (200K)", "tool_call": true, "attachment": true, "reasoning": true, "temperature": true, "limit": { "context": 200000, "output": 64000 }, "price": { "input": 15, "output": 75 }, "options": { "thinking": { "type": "enabled", "budgetTokens": 32000 } } }, "claude-sonnet-4-5-2": { // not an error, my deployment name on Azure "id": "claude-sonnet-4-5-2", "name": "Claude Sonnet 4.5 (1M)", "tool_call": true, "attachment": true, "reasoning": true, "temperature": true, "limit": { "context": 1000000, "output": 32000 }, "price": { "input": 6, "output": 22.5 }, "options": { "thinking": { "type": "enabled", "budgetTokens": 16000 } } } } }

Conv was like :
[assistant] sub agent : finished
[assistant thinking] explore task finished, starting dev task (sub agent)
assistant were then starting the new sub agent => all messages must have non empty content

Note: Azure Anthropic is the exact same implementation than Anthropic API, same headers, same way to send API key, ...

@BouquetAntoine commented on GitHub (Jan 9, 2026): Hi, i don't know if this is deployed or not but got the same error on last (1.1.8) version, session not shareable as used under WSL and "copied to clipboard" = no copy. I'm using Azure Inference for Anthropic `"azure-anthropic": { "name": "Azure AI Foundry (Anthropic)", "npm": "@ai-sdk/anthropic", "options": { "baseURL": "https://XXXXXXXXXXXXXX-resource.services.ai.azure.com/anthropic/v1", "headers": { "anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,token-efficient-tools-2025-02-19,context-1m-2025-08-07,context-management-2025-06-27,tool-search-tool-2025-10-19,tool-examples-2025-10-29" } }, "models": { "claude-opus-4-5-20251101": { "id": "claude-opus-4-5-20251101", "name": "Claude Opus 4.5 (200K)", "tool_call": true, "attachment": true, "reasoning": true, "temperature": true, "limit": { "context": 200000, "output": 64000 }, "price": { "input": 15, "output": 75 }, "options": { "thinking": { "type": "enabled", "budgetTokens": 32000 } } }, "claude-sonnet-4-5-2": { // not an error, my deployment name on Azure "id": "claude-sonnet-4-5-2", "name": "Claude Sonnet 4.5 (1M)", "tool_call": true, "attachment": true, "reasoning": true, "temperature": true, "limit": { "context": 1000000, "output": 32000 }, "price": { "input": 6, "output": 22.5 }, "options": { "thinking": { "type": "enabled", "budgetTokens": 16000 } } } } }` Conv was like : [assistant] sub agent : finished [assistant thinking] explore task finished, starting dev task (sub agent) assistant were then starting the new sub agent => all messages must have non empty content Note: Azure Anthropic is the exact same implementation than Anthropic API, same headers, same way to send API key, ...
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#3998