{"record":{"id":"370f9d022a2832c0","repo":"HKUDS/DeepTutor","slug":"friendly-error-response-status-code","errorCode":null,"errorMessage":"{friendly_error(response.status_code)}","messagePattern":"\\{friendly_error\\(response\\.status_code\\)\\}","errorType":"http","errorClass":"CodexHTTPError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/llm/provider_core/openai_codex_provider.py","lineNumber":227,"sourceCode":"async def _request_codex(\n    url: str,\n    headers: dict[str, str],\n    body: dict[str, Any],\n    verify: bool,\n    on_content_delta: Callable[[str], Awaitable[None]] | None = None,\n) -> tuple[str, list[ToolCallRequest], str]:\n    async with httpx.AsyncClient(timeout=60.0, verify=verify) as client:\n        async with client.stream(\"POST\", url, headers=headers, json=body) as response:\n            if response.status_code != 200:\n                raw = await response.aread()\n                # Kept out of the reply the learner sees, but an operator cannot\n                # diagnose an upstream rejection without the body.\n                logger.debug(\n                    \"Codex API returned HTTP {}: {}\",\n                    response.status_code,\n                    raw.decode(\"utf-8\", \"ignore\")[:500],\n                )\n                raise CodexHTTPError(\n                    response.status_code,\n                    _friendly_error(response.status_code),\n                )\n            return await consume_sse(response, on_content_delta)\n\n\ndef _prompt_cache_key(messages: list[dict[str, Any]]) -> str:\n    raw = json.dumps(messages, ensure_ascii=True, sort_keys=True)\n    return hashlib.sha256(raw.encode(\"utf-8\")).hexdigest()\n\n\ndef _friendly_error(status_code: int) -> str:\n    if status_code == 401:\n        return \"Codex login expired. The session was refreshed; retry this request.\"\n    if status_code == 403:\n        return \"This Codex account is not allowed to make the requested call.\"\n    if status_code == 429:\n        return \"Codex usage quota exceeded or rate limit triggered. Please try again later.\"","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/llm/provider_core/openai_codex_provider.py#L209-L245","documentation":"When the Codex SSE endpoint returns a non-success HTTP status, _request_codex raises CodexHTTPError with a friendly, status-specific message; the raw body (first 500 bytes) is logged at debug level so operators can diagnose without leaking it to users.","triggerScenarios":"chat/chat_stream → _call_codex → _request_codex receives e.g. 401 (expired token), 429 (rate limit), 5xx (upstream failure).","commonSituations":"Rate limits during heavy usage; expired session tokens; OpenAI-side incidents; malformed model names producing 404.","solutions":["Match the friendly message to the status: 429 → back off and retry; 401 → re-login; 5xx → retry later.","Enable debug logging to see the truncated response body.","Check model name and request size if errors are consistent."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    resp = await provider.chat(messages)\nexcept CodexHTTPError as e:\n    if e.status_code == 429:\n        await asyncio.sleep(backoff()); retry()\n    elif e.status_code >= 500:\n        retry_later()\n    else:\n        raise  # 4xx needs user action","preventionTips":["Map status codes to policies: retry 429/5xx, escalate 401/403/404","Enable debug logging in staging to capture truncated bodies","Circuit-break after repeated failures to avoid hammering upstream"],"tags":["http-error","codex","upstream"],"backgroundTag":"llm-api-http-error","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}