Skip to content

Empty message content causes Anthropic API errors during compaction #6446

@Melodeiro

Description

@Melodeiro

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions