fix(client): replace empty text content block with placeholder
When the LLM returns an end_turn with no text content after a tool
call, the session stores Message{Content:"", StructuredContent:""}.
On the next user turn, buildMessageParam() hit the plain-text branch
and called NewTextBlock(""), which the Anthropic API rejects with:
400: "messages: text content blocks must be non-empty"
Replace empty Content with "..." before building the text block.
This preserves the alternating user/assistant message structure
required by the API without introducing invalid empty blocks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 files changed, 9 insertions(+), 3 deletions(-) M internal/llm/client.go
M internal/llm/client.go => internal/llm/client.go +9 -3
@@ 769,11 769,17 @@ func buildMessageParam(msg Message) anthropic.MessageParam { } if msg.StructuredContent == "" { // Plain text message. // Plain text message. Guard against empty text blocks — the Anthropic API // rejects any content block with text:"". This can happen when an assistant // end_turn after tool results has no text (Claude acknowledged silently). content := msg.Content if content == "" { content = "..." } if role == anthropic.MessageParamRoleUser { return anthropic.NewUserMessage(anthropic.NewTextBlock(msg.Content)) return anthropic.NewUserMessage(anthropic.NewTextBlock(content)) } return anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content)) return anthropic.NewAssistantMessage(anthropic.NewTextBlock(content)) } // Structured content: unmarshal and rebuild content blocks.