From 058ce1d1d7355de2ec74e8aada56edd6d3e15eb5 Mon Sep 17 00:00:00 2001 From: argenis de la rosa Date: Sun, 1 Mar 2026 13:31:33 -0500 Subject: [PATCH] fix(anthropic): ignore empty text content blocks --- src/providers/anthropic.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/providers/anthropic.rs b/src/providers/anthropic.rs index b762ef5f4..862803eef 100644 --- a/src/providers/anthropic.rs +++ b/src/providers/anthropic.rs @@ -408,8 +408,9 @@ impl AnthropicProvider { response .content .into_iter() - .find(|c| c.kind == "text") - .and_then(|c| c.text) + .filter(|c| c.kind == "text") + .filter_map(|c| c.text.map(|text| text.trim().to_string())) + .find(|text| !text.is_empty()) .ok_or_else(|| anyhow::anyhow!("No response from Anthropic")) } @@ -1413,6 +1414,36 @@ mod tests { assert!(result.usage.is_none()); } + #[test] + fn parse_text_response_ignores_empty_and_whitespace_text_blocks() { + let json = r#"{ + "content": [ + {"type": "text", "text": ""}, + {"type": "text", "text": " \n "}, + {"type": "text", "text": " final answer "} + ] + }"#; + let response: ChatResponse = serde_json::from_str(json).unwrap(); + + let parsed = AnthropicProvider::parse_text_response(response).unwrap(); + assert_eq!(parsed, "final answer"); + } + + #[test] + fn parse_text_response_rejects_empty_or_whitespace_only_text_blocks() { + let json = r#"{ + "content": [ + {"type": "text", "text": ""}, + {"type": "text", "text": " \n "}, + {"type": "tool_use", "id": "tool_1", "name": "shell"} + ] + }"#; + let response: ChatResponse = serde_json::from_str(json).unwrap(); + + let err = AnthropicProvider::parse_text_response(response).unwrap_err(); + assert!(err.to_string().contains("No response from Anthropic")); + } + #[test] fn capabilities_reports_vision_and_native_tool_calling() { let provider = AnthropicProvider::new(Some("test-key"));