BerriAI/litellm · error · HTTPException
max_budget must be a non-negative finite number. Received: {
Error message
max_budget must be a non-negative finite number. Received: {max_budget} What it means
_validate_max_budget is the shared guard used on key update paths to reject max_budget values that are negative, NaN, or infinite (the finiteness check closes the nan < 0 == False loophole from GHSA-2rv4-xv66-fpjg). It raises HTTP 400 echoing the bad value. It fires on /key/update-style flows wherever budgets are re-validated before persisting.
Source
Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:2135
if data.team_id is None:
return False
if existing_key_row.team_id is None:
return True
return data.team_id != existing_key_row.team_id
def _validate_max_budget(max_budget: float | None) -> None:
"""
Validate that max_budget is not negative.
Args:
max_budget: The max_budget value to validate
Raises:
HTTPException: If max_budget is negative
"""
if max_budget is not None and (not math.isfinite(max_budget) or max_budget < 0):
raise HTTPException(
status_code=400,
detail={"error": f"max_budget must be a non-negative finite number. Received: {max_budget}"},
)
async def _get_and_validate_existing_key(
token: str | None, prisma_client: PrismaClient | None, key_alias: str | None = None
) -> LiteLLM_VerificationToken:
"""
Get existing key from database and validate it exists.
Args:
token: The key token to look up
prisma_client: Prisma client instance
key_alias: Alias to look the key up by when token is not provided
Returns:
LiteLLM_VerificationToken: The existing key rowView on GitHub (pinned to 77b7c6c40c)
Solutions
- Send a finite max_budget >= 0, or omit/null it when no cap is intended
- Clamp computed budgets: max(0, limit - spent) before calling the API
- If re-submitting stored values, skip or sanitize non-finite ones first (math.isfinite check)
- For 'unlimited', omit max_budget rather than using a huge/inf sentinel
Example fix
# before
new_budget = user_limit - total_spent # can be -12.5
await client.post("/key/update", json={"key": k, "max_budget": new_budget})
# after
import math
new_budget = max(0.0, user_limit - total_spent)
if not math.isfinite(new_budget):
new_budget = None
await client.post("/key/update", json={"key": k, "max_budget": new_budget}) Defensive patterns
Strategy: validation
Validate before calling
import math
def sanitize_budget(v):
if v is None:
return None
v = float(v)
return v if math.isfinite(v) and v >= 0 else None # None = no limit
payload["max_budget"] = sanitize_budget(computed_limit) Type guard
def is_finite_non_negative(v: object) -> bool:
return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) and v >= 0 Try / catch
try:
r = await client.post("/key/update", json=payload)
except httpx.HTTPStatusError as e:
if e.response.status_code == 400 and "max_budget" in e.response.text:
payload["max_budget"] = max(0, math.floor(payload["max_budget"]))
r = await client.post("/key/update", json=payload)
else:
raise Prevention
- Clamp arithmetic budgets: max(0, limit - spent) before any API call
- Represent 'unlimited' as an omitted/null max_budget, never inf or a huge sentinel
- Guard against NaN in deserialized JSON payloads (json.loads accepts NaN/Infinity by default)
When it happens
Trigger: POST /key/update with max_budget: -50; sending NaN/Infinity in JSON (Python json accepts NaN, Infinity tokens); computing max_budget as a subtraction that underflows to a negative; unsetting via 0-like sentinel values such as float('-inf') intended to mean 'no limit' (use null/omit instead).
Common situations: Billing scripts deriving max_budget = spend_limit - spent which goes negative once over budget; configs using .inf YAML literals for 'unlimited'; migrating old keys whose stored budget is already NaN and re-submitting it verbatim on update.
Related errors
- soft_budget must be a non-negative finite number. Received:
- either key or key_alias must be provided
- max_budget cannot be negative. Received: {data.max_budget}
- soft_budget cannot be negative. Received: {data.soft_budget}
- soft_budget ({data.soft_budget}) must be strictly lower than
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/227f15aa523657e1.
Report an issue: GitHub.