~kris/9p

llm9p

35c20f3e6e008970ccdc9ea460682c578bd6d35a — pdfinn 6 months ago 0afa911
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.