HKUDS/DeepTutor · error · LLMAPIError
Anthropic API error: unexpected response payload
Error message
Anthropic API error: unexpected response payload
What it means
After a 200 response, _anthropic_complete walks result['content'][0]['text'] through a chain of isinstance/Mapping casts; if any level is missing or not a string, LLMAPIError 'unexpected response payload' is raised with the (successful) status code. It means the endpoint returned 200 JSON that does not match Anthropic's Messages schema.
Source
Thrown at deeptutor/services/llm/cloud_provider.py:721
async with session.post(url, headers=headers, json=data) as response:
if response.status != 200:
error_text = await response.text()
raise LLMAPIError(
f"Anthropic API error: {error_text}",
status_code=response.status,
provider="anthropic",
)
result = cast(dict[str, object], await response.json())
content_items = result.get("content")
if isinstance(content_items, list) and content_items:
content_list = cast(list[object], content_items)
first_item = content_list[0]
if isinstance(first_item, Mapping):
text = cast(Mapping[str, object], first_item).get("text")
if isinstance(text, str):
return text
raise LLMAPIError(
"Anthropic API error: unexpected response payload",
status_code=response.status,
provider="anthropic",
)
async def _anthropic_stream(
model: str,
prompt: str,
system_prompt: str,
api_key: str | None,
base_url: str | None,
messages: list[dict[str, object]] | None = None,
max_tokens: int | None = None,
temperature: float | None = None,
) -> AsyncGenerator[str, None]:
"""Anthropic (Claude) API streaming."""
import jsonView on GitHub (pinned to 3e82f13042)
Solutions
- Log the full response JSON once to compare against the documented Messages schema.
- Fix mock/proxy fixtures to include content: [{type: 'text', text: '...'}].
- If hitting the real API, confirm base_url is not pointed at a non-Anthropic endpoint.
- Report/patch if a provider revision changed the payload shape.
Example fix
// before
# mock server
{"content": [{"type": "text", "body": "hi"}]}
# after
{"content": [{"type": "text", "text": "hi"}]} Defensive patterns
Strategy: type-guard
Validate before calling
# Not applicable (server-side payload); pin conforming endpoints only
Type guard
def is_anthropic_messages_payload(result: dict) -> bool:
content = result.get("content")
return (
isinstance(content, list)
and bool(content)
and isinstance(content[0], dict)
and isinstance(content[0].get("text"), str)
) Try / catch
try:
out = await complete(prompt=p, binding="anthropic", model=m, api_key=k)
except LLMAPIError as e:
if "unexpected response payload" in str(e):
log.error("nonstandard Anthropic endpoint; raw body needed")
raise Prevention
- Validate mock/proxy fixtures against the documented Messages schema.
- Log raw bodies when integrating Anthropic-compatible gateways.
- Avoid middleboxes that rewrite response JSON.
When it happens
Trigger: A mock, proxy, or Anthropic-compatible gateway returning {"content": []} or content items without a text field; empty completion where Anthropic returns an empty content array (possible with certain stop/tool configurations); schema drift after an API revision.
Common situations: Testing against stub servers with hand-rolled response fixtures; middleboxes rewriting the body; very rare real-API schema changes or empty model outputs.
Related errors
- Cohere API error: unexpected response payload
- Cloud completion failed: no valid configuration
- Anthropic API key is missing from the active LLM profile.
- Anthropic API error: {error_text}
- Anthropic stream error: {error_text}
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/941b6912440e5614.
Report an issue: GitHub.