HKUDS/DeepTutor · error · LLMAPIError

Cohere API error: unexpected response payload

Error message

Cohere API error: unexpected response payload

What it means

After a 200 response, _cohere_complete expects result['text'] to be a string; any other top-level shape (missing 'text', nested generations, error object returned with 200) triggers LLMAPIError 'unexpected response payload'. It is the Cohere analogue of the schema-drift guard: status was fine, payload was not.

Source

Thrown at deeptutor/services/llm/cloud_provider.py:865

    timeout = aiohttp.ClientTimeout(total=120)
    connector = _get_aiohttp_connector()
    async with aiohttp.ClientSession(
        timeout=timeout, connector=connector, trust_env=True
    ) as session:
        async with session.post(url, headers=headers, json=data) as response:
            if response.status != 200:
                error_text = await response.text()
                raise LLMAPIError(
                    f"Cohere API error: {error_text}",
                    status_code=response.status,
                    provider="cohere",
                )

            result = cast(dict[str, object], await response.json())
            text = result.get("text")
            if isinstance(text, str):
                return text
            raise LLMAPIError(
                "Cohere API error: unexpected response payload",
                status_code=response.status,
                provider="cohere",
            )


async def fetch_models(
    base_url: str,
    api_key: str | None = None,
    binding: str = "openai",
) -> list[str]:
    """
    Fetch available models from cloud provider.

    Args:
        base_url: API endpoint URL
        api_key: API key
        binding: Provider type (openai, anthropic)

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Log the raw JSON body to see which shape is returned.
  2. Point base_url at the endpoint version whose schema this code expects (text field), or adapt parsing upstream.
  3. Fix mock fixtures to include a top-level "text" string.
  4. Check for provider middleware rewriting bodies.

Example fix

// before
# stub response
{"generations": [{"text": "hi"}]}

# after
{"text": "hi"}
Defensive patterns

Strategy: type-guard

Validate before calling

# Not applicable (server payload); ensure the endpoint returns the legacy shape:
# assert base_url in known_v1_endpoints

Type guard

def is_cohere_text_payload(result: dict) -> bool:
    return isinstance(result.get("text"), str)

Try / catch

try:
    out = await complete(prompt=p, binding="cohere", model=m, api_key=k)
except LLMAPIError as e:
    if "unexpected response payload" in str(e):
        log.error("endpoint returned non-v1 Cohere shape; check base_url")
    raise

Prevention

When it happens

Trigger: Endpoint returns the newer Cohere v2 shape ({message: {content: [...]}}) instead of the legacy {text}; a proxy normalizes responses differently; mock fixtures missing the text key; model variants that return generations arrays.

Common situations: Cohere API version drift between v1 generate and v2 chat endpoints; hand-written test stubs; gateways aggregating multiple providers into one schema.

Related errors


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