{"record":{"id":"cc4ca02949810e0b","repo":"HKUDS/DeepTutor","slug":"cohere-api-error-unexpected-response-payload","errorCode":null,"errorMessage":"Cohere API error: unexpected response payload","messagePattern":"Cohere API error: unexpected response payload","errorType":"http","errorClass":"LLMAPIError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/llm/cloud_provider.py","lineNumber":865,"sourceCode":"    timeout = aiohttp.ClientTimeout(total=120)\n    connector = _get_aiohttp_connector()\n    async with aiohttp.ClientSession(\n        timeout=timeout, connector=connector, trust_env=True\n    ) as session:\n        async with session.post(url, headers=headers, json=data) as response:\n            if response.status != 200:\n                error_text = await response.text()\n                raise LLMAPIError(\n                    f\"Cohere API error: {error_text}\",\n                    status_code=response.status,\n                    provider=\"cohere\",\n                )\n\n            result = cast(dict[str, object], await response.json())\n            text = result.get(\"text\")\n            if isinstance(text, str):\n                return text\n            raise LLMAPIError(\n                \"Cohere API error: unexpected response payload\",\n                status_code=response.status,\n                provider=\"cohere\",\n            )\n\n\nasync def fetch_models(\n    base_url: str,\n    api_key: str | None = None,\n    binding: str = \"openai\",\n) -> list[str]:\n    \"\"\"\n    Fetch available models from cloud provider.\n\n    Args:\n        base_url: API endpoint URL\n        api_key: API key\n        binding: Provider type (openai, anthropic)","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/llm/cloud_provider.py#L847-L883","documentation":"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.","triggerScenarios":"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.","commonSituations":"Cohere API version drift between v1 generate and v2 chat endpoints; hand-written test stubs; gateways aggregating multiple providers into one schema.","solutions":["Log the raw JSON body to see which shape is returned.","Point base_url at the endpoint version whose schema this code expects (text field), or adapt parsing upstream.","Fix mock fixtures to include a top-level \"text\" string.","Check for provider middleware rewriting bodies."],"exampleFix":"// before\n# stub response\n{\"generations\": [{\"text\": \"hi\"}]}\n\n# after\n{\"text\": \"hi\"}","handlingStrategy":"type-guard","validationCode":"# Not applicable (server payload); ensure the endpoint returns the legacy shape:\n# assert base_url in known_v1_endpoints","typeGuard":"def is_cohere_text_payload(result: dict) -> bool:\n    return isinstance(result.get(\"text\"), str)","tryCatchPattern":"try:\n    out = await complete(prompt=p, binding=\"cohere\", model=m, api_key=k)\nexcept LLMAPIError as e:\n    if \"unexpected response payload\" in str(e):\n        log.error(\"endpoint returned non-v1 Cohere shape; check base_url\")\n    raise","preventionTips":["Pin the Cohere endpoint version your parser targets in base_url.","Type-check mock fixtures include a top-level 'text' string.","Watch Cohere release notes for schema changes (v1 generate vs v2 chat)."],"tags":["llm","cohere","unexpected-payload","schema"],"backgroundTag":"unexpected-response-schema","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}