BerriAI/litellm · warning · ValueError
limit must be an integer
Error message
limit must be an integer
What it means
ValueError from LiteLLMDatabase.get_usage_data when the limit parameter cannot be converted with int(limit) — either a TypeError (type has no int conversion, e.g. a list or None-like object passed despite the None check) or ValueError (a non-numeric string like 'all' or '1.5.2'). This is input validation before the SQL LIMIT clause is appended.
Source
Thrown at litellm/integrations/cloudzero/database.py:92
FROM "LiteLLM_DailyUserSpend" dus
LEFT JOIN "LiteLLM_VerificationToken" vt ON dus.api_key = vt.token
LEFT JOIN "LiteLLM_TeamTable" tt ON vt.team_id = tt.team_id
LEFT JOIN "LiteLLM_UserTable" ut ON dus.user_id = ut.user_id
WHERE ($1::timestamptz IS NULL OR dus.updated_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR dus.updated_at <= $2::timestamptz)
ORDER BY dus.date DESC, dus.created_at DESC
"""
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
- Pass limit as an actual int, or omit it entirely for no limit
- Coerce at the boundary: int(str_limit) inside your own try/except before calling
- Reject non-numeric strings early with a clear 400-style error in your caller
- For 'unlimited' semantics, pass limit=None, not a sentinel string
Example fix
# before
df = await db.get_usage_data(limit=request.args.get("limit")) # 'all' → ValueError
# after
raw = request.args.get("limit")
limit = int(raw) if raw and raw.isdigit() else None
df = await db.get_usage_data(limit=limit) Defensive patterns
Strategy: validation
Validate before calling
def coerce_limit(raw) -> int | None:
if raw is None:
return None
try:
return int(raw)
except (TypeError, ValueError):
return None # or raise your own 400 error Type guard
def is_int_like_limit(raw) -> bool:
if raw is None:
return True
if isinstance(raw, bool):
return False
try:
int(raw)
return True
except (TypeError, ValueError):
return False Try / catch
try:
df = await db.get_usage_data(limit=raw_limit)
except ValueError as e:
if "limit must be an integer" in str(e):
df = await db.get_usage_data(limit=None)
else:
raise Prevention
- Cast limit to int at the API/config boundary
- Use None for unlimited rather than sentinel strings
- Validate query params with pydantic before they reach the database layer
When it happens
Trigger: Calling get_usage_data(limit='all'), limit='1.5.2', or limit=[100] — typically when the value arrives from a CLI flag, HTTP query param, or config file without prior type coercion. Floats like '1.5' fail int() too even though they look numeric.
Common situations: Script or scheduler config passing limit as a string from YAML/JSON; users typing 'unlimited'/'all'; API query params not cast to int at the boundary.
Related errors
- Database not connected. Connect a database to your proxy - h
- Error retrieving usage data: {e}
- api base needs to be a string. api_base={api_base}
- dynamic_api_key needs to be a string. Got type={type(dynamic
- Invalid image_url type: {type(image_url).__name__}. Expected
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/8115e5927975e262.
Report an issue: GitHub.