diff --git a/pkg/providers/anthropic_messages/provider.go b/pkg/providers/anthropic_messages/provider.go index 55bb41e66..6894b6d61 100644 --- a/pkg/providers/anthropic_messages/provider.go +++ b/pkg/providers/anthropic_messages/provider.go @@ -253,6 +253,14 @@ func buildRequestBody( content = append(content, toolUse) } + // Skip empty assistant messages that have no content and no valid tool calls. + // This prevents breaking the Anthropic API requirement where a user message + // containing tool results must immediately follow the assistant message + // containing the corresponding tool calls. + if len(content) == 0 { + continue + } + apiMessages = append(apiMessages, map[string]any{ "role": "assistant", "content": content, diff --git a/pkg/providers/anthropic_messages/provider_test.go b/pkg/providers/anthropic_messages/provider_test.go index 39bc48117..153843bb1 100644 --- a/pkg/providers/anthropic_messages/provider_test.go +++ b/pkg/providers/anthropic_messages/provider_test.go @@ -506,6 +506,25 @@ func TestBuildRequestBodyEdgeCases(t *testing.T) { }, wantErr: false, }, + { + name: "skip empty assistant messages", + messages: []Message{ + {Role: "user", Content: "hello"}, + {Role: "assistant", Content: "", ToolCalls: []ToolCall{}}, // Should be skipped + {Role: "assistant", Content: "valid message", ToolCalls: []ToolCall{}}, + {Role: "assistant", Content: "", ToolCalls: []ToolCall{ + {ID: "tool-valid", Name: "test_tool", Arguments: map[string]any{"arg": "value"}}, + }}, + {Role: "assistant", Content: "", ToolCalls: []ToolCall{ + {ID: "tool-empty", Name: "", Arguments: map[string]any{"ignored": true}}, + }}, // Should be skipped because tool call is empty and content is empty + }, + model: "test-model", + options: map[string]any{ + "max_tokens": 8192, + }, + wantErr: false, + }, } for _, tt := range tests { @@ -558,6 +577,20 @@ func TestBuildRequestBodyEdgeCases(t *testing.T) { t.Fatalf("tool_use id = %v, want %q", gotID, "tool-valid") } } + + if tt.name == "skip empty assistant messages" { + messages, ok := got["messages"].([]any) + if !ok { + t.Fatalf("messages is not []any") + } + if len(messages) != 3 { + t.Fatalf( + "expected 3 API messages (user, assistant with text, assistant with tool_use), got %d: %#v", + len(messages), + messages, + ) + } + } }) } }