BerriAI/litellm · error · ValueError
Invalid model_max_budget: {e}. Example of valid model_max_bu
Error message
Invalid model_max_budget: {e}. Example of valid model_max_budget: https://docs.litellm.ai/docs/proxy/users What it means
Structural validation of model_max_budget: it must be a mapping of model-name (str) -> BudgetConfig-compatible dict, where nested budget_limit must be numeric or a numeric string ('10' is coerced, 'ten' is not). Any exception during iteration or BudgetConfig(**info) construction — non-str model keys, missing/invalid fields, unparseable budget_limit — is re-raised as this ValueError with the original error appended and a docs link. It also wraps the enterprise-license error, so read the embedded text to tell the two apart.
Source
Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:6709
return
if model_max_budget is not None:
from litellm.proxy.proxy_server import CommonProxyErrors, premium_user
if premium_user is not True:
raise ValueError(
f"You must have an enterprise license to set model_max_budget. {CommonProxyErrors.not_premium_user.value}"
)
for _model, _budget_info in model_max_budget.items():
assert isinstance(_model, str)
# Normalize to dict (Pydantic may already parse nested values as BudgetConfig)
_info = _budget_info.model_dump() if hasattr(_budget_info, "model_dump") else dict(_budget_info)
# /CRUD endpoints can pass budget_limit as a string, so we need to convert it to a float
if "budget_limit" in _info:
_info["budget_limit"] = float(_info["budget_limit"])
BudgetConfig(**_info)
except Exception as e:
raise ValueError(
f"Invalid model_max_budget: {e}. Example of valid model_max_budget: https://docs.litellm.ai/docs/proxy/users"
)
View on GitHub (pinned to 77b7c6c40c)
Solutions
- Match the documented shape: {'<model-name>': {'budget_limit': <number>, 'budget_duration': '1d'|'1mo'|..., 'budget_id': <optional str>}}
- Ensure model names are plain strings and budget_limit is a number or numeric string
- If the embedded text says 'enterprise license', fix licensing per that error instead of the data shape
- Validate locally with the same rules before sending (see validationCode)
Example fix
# before
await client.post('/key/generate', json={
'model_max_budget': {'gpt-4o': {'budget_limit': 'ten dollars'}} # ValueError: Invalid model_max_budget...
})
# after
await client.post('/key/generate', json={
'model_max_budget': {'gpt-4o': {'budget_limit': 10.0, 'budget_duration': '1d', 'budget_id': 'mb-1'}}
}) Defensive patterns
Strategy: validation
Validate before calling
def validate_model_max_budget(mmb: dict) -> None:
if mmb in (None, {}):
return
if not isinstance(mmb, dict):
raise TypeError('model_max_budget must be a dict of model -> budget config')
for model, info in mmb.items():
if not isinstance(model, str):
raise TypeError(f'model key must be str, got {type(model).__name__}')
limit = info.get('budget_limit') if isinstance(info, dict) else None
if limit is None or isinstance(limit, bool):
raise ValueError(f'{model}: budget_limit required')
float(limit) # must be numeric or numeric string Type guard
def is_valid_model_max_budget(mmb: object) -> bool:
if mmb is None or mmb == {}:
return True
if not isinstance(mmb, dict):
return False
for model, info in mmb.items():
if not isinstance(model, str) or not isinstance(info, dict):
return False
try:
float(info.get('budget_limit'))
except (TypeError, ValueError):
return False
return True Try / catch
try:
await client.post('/key/generate', json=payload)
except httpx.HTTPStatusError as e:
body = e.response.text
if 'enterprise license' in body:
raise RuntimeError('license missing: set LITELLM_LICENSE') from e
if 'Invalid model_max_budget' in body:
raise ValueError(f'bad model_max_budget shape: {payload.get("model_max_budget")}') from e
raise Prevention
- Construct model_max_budget via a typed dataclass/Pydantic model mirroring BudgetConfig
- Coerce budget_limit to float at the client; never pass free text
- Read the embedded cause — the same outer message wraps both license and shape failures
When it happens
Trigger: 'model_max_budget': 'gpt-4o' (a plain string instead of a dict); {'gpt-4o': {'budget_limit': 'unlimited'}}; values passed as Pydantic BudgetConfig objects that fail a required field; keys like {('gpt','4o'): {...}} (tuple model name fails the isinstance str assert).
Common situations: Config YAML where indentation makes model_max_budget a list or scalar; JSON numbers arriving as strings from form data; partial copy-paste of the enterprise example missing budget_duration.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid sort order. Must be 'asc' or 'desc'
- Invalid key format.
- You must have an enterprise license to set model_max_budget.
- mcp_tools_config must be a list of dictionaries
- GitHub source must include 'repo' field (e.g., 'org/repo')
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/37f8fe9cfed76287.
Report an issue: GitHub.