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.

Solutions

  1. Log the full response JSON once to compare against the documented Messages schema.
  2. Fix mock/proxy fixtures to include content: [{type: 'text', text: '...'}].
  3. If hitting the real API, confirm base_url is not pointed at a non-Anthropic endpoint.
  4. 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

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


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/941b6912440e5614. Report an issue: GitHub.

Appendix: 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 json

View on GitHub (pinned to 3e82f13042)