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

  1. First fix the vendored client so _http_request returns the httpx response (without this, res is always None)
  2. Log res.status_code and the parsed body before raising to capture the server's reason
  3. 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

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


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/0bb8fd2db198c065. Report an issue: GitHub.