BerriAI/litellm · error · Exception
res.json().get("error", res.text)
Error message
res.json().get("error", res.text) What it means
send_request treats any status other than exactly 200 as fatal, raising an Exception whose message is the server's JSON "error" field (falling back to raw body text). Grounded caveat: in this vendored copy _http_request returns None (missing return statement), so `res.status_code` raises AttributeError before this line can ever execute — the branch is currently dead code until the client is fixed to return the response.
Source
Thrown at litellm/integrations/deepeval/api.py:98
def send_request(self, method: HttpMethods, endpoint: Endpoints, body=None, params=None):
url: Final = f"{self.base_api_url}{endpoint.value}"
res: Final = self._http_request(
method=method.value,
url=url,
headers=self._headers,
json=body,
params=params,
)
if res.status_code == 200:
try:
return res.json()
except ValueError:
return res.text
else:
verbose_logger.debug(res.json())
raise Exception(res.json().get("error", res.text))
async def a_send_request(self, method: HttpMethods, endpoint: Endpoints, body=None, params=None):
if method != HttpMethods.POST:
raise Exception("Only POST requests are supported")
url: Final = f"{self.base_api_url}{endpoint.value}"
try:
await self.async_http_handler.post(
url=url,
headers=self._headers,
json=body,
params=params,
)
except httpx.HTTPStatusError as e:
raise Exception(f"DeepEval logging error: {e.response.text}")
except Exception as e:
raise e
View on GitHub (pinned to 77b7c6c40c)
Solutions
- First fix the vendored client so _http_request returns the httpx response (without this, res is always None)
- Log res.status_code and the parsed body before raising to capture the server's reason
- For auth errors re-check CONFIDENT_API_KEY; for 429 add backoff/batching; for 201-style replies widen the success check
Example fix
# before
raise Exception(res.json().get("error", res.text))
# after
if res.status_code >= 400:
try:
msg = res.json().get("error", res.text)
except ValueError:
msg = res.text
raise Exception(f"DeepEval API {res.status_code}: {msg}")
return res.json() if res.headers.get("content-type", "").startswith("application/json") else res.text Defensive patterns
Strategy: try-catch
Validate before calling
# Nothing to pre-validate server-side; guard the client contract instead:
from litellm.integrations.deepeval.api import HttpMethods
def valid_send_args(method, endpoint) -> bool:
return method is HttpMethods.POST and hasattr(endpoint, "value") Try / catch
try:
result = api.send_request(HttpMethods.POST, Endpoints.TRACING_ENDPOINT, body=body)
except Exception as e:
body = str(e)
if "error" in body or body.startswith("{"):
litellm.verbose_logger.warning("confident-ai rejected payload: %s", body)
raise Prevention
- Patch the vendored _http_request to return the response before relying on status handling
- Never assume exactly-200 semantics on third-party APIs; log status codes explicitly
- Keep the deepeval dependency pinned and diff vendored copies on upgrade
When it happens
Trigger: After patching the missing return: Confident AI answering 201/202 or any 4xx/429/5xx; a payload the server accepts-but-rejects (400 with an "error" JSON body); rate limiting during batch trace uploads.
Common situations: Server contract drift (201 Created responses), expired keys, quota exhaustion — plus, today, simply reaching this code path via the un-patched vendored client.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- DeepEval logging error: {e.response.text}
- Only POST requests are supported
- Response from datadog API status_code: {response.status_code
- DataDogLLMObs: Unexpected response - status_code: {response.
- Please set 'CONFIDENT_API_KEY=<>' in your environment variab
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/0bb8fd2db198c065.
Report an issue: GitHub.