{"record":{"id":"ba45be2d57aed196","repo":"BerriAI/litellm","slug":"openai-moderation-api-request-failed","errorCode":null,"errorMessage":"OpenAI Moderation API request failed","messagePattern":"OpenAI Moderation API request failed","errorType":"http","errorClass":"HTTPException","httpStatus":null,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/openai/moderations.py","lineNumber":123,"sourceCode":"        Make a request to the OpenAI Moderation API.\n        \"\"\"\n        request_body: Final = {\"model\": self.model, \"input\": input_text}\n\n        verbose_proxy_logger.debug(\"OpenAI Moderation guard request: %s\", request_body)\n\n        response: Final = await self.async_handler.post(\n            url=f\"{self.api_base}/moderations\",\n            headers={\n                \"Authorization\": f\"Bearer {self.api_key}\",\n                \"Content-Type\": \"application/json\",\n            },\n            json=request_body,\n        )\n\n        verbose_proxy_logger.debug(\"OpenAI Moderation guard response: %s\", response.json())\n\n        if response.status_code != 200:\n            raise HTTPException(\n                status_code=response.status_code,\n                detail={\n                    \"error\": \"OpenAI Moderation API request failed\",\n                    \"details\": response.text,\n                },\n            )\n\n        from litellm.types.llms.openai import OpenAIModerationResponse\n\n        return OpenAIModerationResponse(**response.json())\n\n    def _check_moderation_result(self, moderation_response: \"OpenAIModerationResponse\") -> None:\n        \"\"\"\n        Check if the moderation response indicates harmful content and raise exception if needed.\n        \"\"\"\n        if not moderation_response.results:\n            return\n","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/openai/moderations.py#L105-L141","documentation":"Runtime HTTPException raised inside OpenAIModerationGuardrail when the POST to {api_base}/moderations returns a non-200 status. The upstream status code is passed through verbatim to the caller, with the response body in detail.details — so a 401 means the guardrail's OpenAI key is bad, 429 means rate-limit/quota, 5xx means OpenAI-side trouble.","triggerScenarios":"Guardrail api_key revoked or invalid → 401; moderation quota exhausted or org rate-limited → 429; OpenAI incident → 5xx; custom api_base pointing at a gateway that rejects the moderations path (404/400)","commonSituations":"Rotating the OpenAI key for model calls but forgetting the guardrail config; Azure/other proxies set as api_base without moderations support; bursty pre-call moderation triggering org-wide rate limits","solutions":["Match the status: 401/403 → fix/rotate the guardrail's api_key; 429 → slow down or reduce guardrail coverage (fewer hooks/models); 5xx → retry later","If using a custom api_base, confirm it implements POST /moderations and accepts the same auth","Add retry with backoff for 429/5xx since moderation calls are read-only and safe to retry","Consider moderating only pre_call (prompts) rather than every chunk if quota is the issue"],"exampleFix":"# before\nresp = await handler.post(url, ...)  # single shot, 429 kills the request\n\n# after: retry transient upstream statuses\nfor attempt in range(4):\n    resp = await self.async_handler.post(url, headers=..., json=request_body)\n    if resp.status_code == 200:\n        break\n    if resp.status_code in (429, 500, 502, 503) and attempt < 3:\n        await asyncio.sleep(float(resp.headers.get(\"retry-after\", 2 ** attempt)))\n        continue\n    raise HTTPException(status_code=resp.status_code, detail={\"error\": \"OpenAI Moderation API request failed\", \"details\": resp.text})","handlingStrategy":"retry","validationCode":"# Operator-side preflight: verify the guardrail key can actually reach moderations\nimport httpx\n\ndef moderation_key_ok(api_key: str, api_base: str = \"https://api.openai.com/v1\") -> bool:\n    r = httpx.post(\n        f\"{api_base}/moderations\",\n        headers={\"Authorization\": f\"Bearer {api_key}\"},\n        json={\"model\": \"omni-moderation-latest\", \"input\": \"ping\"},\n        timeout=10,\n    )\n    return r.status_code == 200","typeGuard":null,"tryCatchPattern":"for attempt in range(4):\n    try:\n        r = await client.post(f\"{PROXY}/chat/completions\", json=payload)\n        r.raise_for_status()\n        break\n    except httpx.HTTPStatusError as e:\n        sc = e.response.status_code\n        # proxy passes the OpenAI moderation status through verbatim\n        if sc in (429, 500, 502, 503) and attempt < 3:\n            await asyncio.sleep(float(e.response.headers.get(\"retry-after\", 2 ** attempt)))\n            continue\n        if sc in (401, 403):\n            alert(\"guardrail OpenAI key invalid\")\n        raise","preventionTips":["Retry only 429/5xx with backoff; treat 401/403 as config alarms","Watch moderation-call volume: pre_call on every request multiplies quota usage","If self-hosting api_base, health-check the /moderations route itself"],"tags":["openai-moderation","guardrails","upstream","http","rate-limit"],"backgroundTag":"upstream-api-error","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}