{"record":{"id":"dafad3e5cf74dd6d","repo":"MemPalace/mempalace","slug":"empty-response-from-anthropic-model-self-model","errorCode":null,"errorMessage":"Empty response from Anthropic (model={self.model})","messagePattern":"Empty response from Anthropic \\(model=(.+?)\\)","errorType":"exception","errorClass":"LLMError","httpStatus":null,"severity":"error","filePath":"mempalace/llm_client.py","lineNumber":435,"sourceCode":"            \"temperature\": 0.1,\n            \"system\": sys_prompt,\n            \"messages\": [{\"role\": \"user\", \"content\": user}],\n        }\n        headers = {\n            \"X-API-Key\": self.api_key,\n            \"anthropic-version\": self.API_VERSION,\n        }\n        data = _http_post_json(\n            f\"{self.endpoint}/v1/messages\", body, headers=headers, timeout=self.timeout\n        )\n        try:\n            text = \"\".join(\n                b.get(\"text\", \"\") for b in data.get(\"content\", []) or [] if b.get(\"type\") == \"text\"\n            )\n        except (AttributeError, TypeError) as e:\n            raise LLMError(f\"Unexpected response shape: {e}\") from e\n        if not text:\n            raise LLMError(f\"Empty response from Anthropic (model={self.model})\")\n        return LLMResponse(text=text, model=self.model, provider=self.name, raw=data)\n\n\n# ==================== FACTORY ====================\n\n\nPROVIDERS: dict[str, type[LLMProvider]] = {\n    \"ollama\": OllamaProvider,\n    \"openai-compat\": OpenAICompatProvider,\n    \"anthropic\": AnthropicProvider,\n}\n\n\ndef get_provider(\n    name: str,\n    model: str,\n    endpoint: Optional[str] = None,\n    api_key: Optional[str] = None,","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/llm_client.py#L417-L453","documentation":"LLMError raised by AnthropicProvider.classify() when the /v1/messages response contains content blocks but joining all type=='text' blocks yields an empty string — e.g. the response consists solely of tool_use or thinking blocks.","triggerScenarios":"A model turn that is entirely a tool_use block with no text; extended thinking consuming the whole response; a text block present but empty after filtering by type.","commonSituations":"Prompts that induce tool calling when the caller wanted prose; thinking-enabled models under max_tokens=2048 spending it all on reasoning; mismatched prompt format causing the model to emit only non-text blocks.","solutions":["Inspect raw response (LLMResponse.raw / provider debug) to see which block types came back","If tool_use came back, adjust the prompt to request a direct textual answer","Increase max_tokens so thinking models still produce a text block","Retry once — occasional empty text turns are transient"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from mempalace.llm_client import LLMError\n\nfor attempt in range(2):\n    try:\n        return provider.classify(s, u)\n    except LLMError as e:\n        if \"Empty response from Anthropic\" in str(e) and attempt == 0:\n            continue\n        raise","preventionTips":["Phrase prompts to demand a textual JSON answer (the provider appends that instruction in json_mode)","Raise max_tokens for thinking-heavy models so a text block still fits","Inspect raw responses when empties recur to spot tool_use-only turns"],"tags":["llm","anthropic","empty-response","models"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}