BerriAI/litellm · error · Exception

Error retrieving usage data: {e}

Error message

Error retrieving usage data: {e}

What it means

Catch-all Exception from LiteLLMDatabase.get_usage_data wrapping the prisma query_raw call or the polars DataFrame construction. The message embeds the underlying error string, which can be a Prisma query error (bad parameter binding, SQL syntax from malformed timestamps), a Postgres connectivity error, or a polars schema-inference failure on the returned rows.

Source

Thrown at litellm/integrations/cloudzero/database.py:101

        params: Final[list[Any]] = [
            start_time_utc,
            end_time_utc,
        ]

        if limit is not None:
            try:
                params.append(int(limit))
            except (TypeError, ValueError):
                raise ValueError("limit must be an integer")
            query += " LIMIT $3"

        try:
            db_response: Final = await client.db.query_raw(query, *params)
            # Convert the response to polars DataFrame with full schema inference
            # This prevents schema mismatch errors when data types vary across rows
            return pl.DataFrame(db_response, infer_schema_length=None)
        except Exception as e:
            raise Exception(f"Error retrieving usage data: {e}")

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Read the embedded error text — it distinguishes auth/connection ('password authentication failed', 'connection refused') from schema issues ('relation does not exist')
  2. Run prisma migration / let the proxy create tables by starting it once with the database connected before exporting
  3. Pass timezone-aware UTC datetimes for start_time_utc/end_time_utc
  4. Retry on transient connection errors with backoff; fix data/schema issues instead of retrying those

Example fix

# before
df = await db.get_usage_data(limit=100, start_time_utc="2026-01-01")  # str not datetime

# after
from datetime import datetime, timezone
df = await db.get_usage_data(
    limit=100,
    start_time_utc=datetime(2026, 1, 1, tzinfo=timezone.utc),
)
Defensive patterns

Strategy: retry

Validate before calling

from datetime import datetime

def valid_utc(dt) -> bool:
    return dt is None or isinstance(dt, datetime)

Type guard

from datetime import datetime

def is_valid_export_window(start, end) -> bool:
    ok = (start is None or isinstance(start, datetime)) and (end is None or isinstance(end, datetime))
    if isinstance(start, datetime) and isinstance(end, datetime):
        ok = ok and start <= end
    return ok

Try / catch

import asyncio

async def export_with_retry(db, **kw):
    for attempt in range(3):
        try:
            return await db.get_usage_data(**kw)
        except Exception as e:
            msg = str(e)
            transient = any(s in msg for s in ("connection", "timeout", "terminating"))
            if transient and attempt < 2:
                await asyncio.sleep(2 ** attempt)
                continue
            raise

Prevention

When it happens

Trigger: Postgres unreachable or credentials rotated; start/end timestamps in a format Prisma cannot bind as $1/$2 parameters; the daily-user-spend table missing because the proxy schema was never migrated; polars failing infer_schema_length=None on inconsistent column types.

Common situations: Database credentials rotated without updating the proxy; running export against a fresh database with no spend table; passing tz-naive vs tz-aware datetimes inconsistently; very old rows with different column types breaking schema inference.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/5551befe9b2e8e5a. Report an issue: GitHub.