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

  1. Match the documented shape: {'<model-name>': {'budget_limit': <number>, 'budget_duration': '1d'|'1mo'|..., 'budget_id': <optional str>}}
  2. Ensure model names are plain strings and budget_limit is a number or numeric string
  3. If the embedded text says 'enterprise license', fix licensing per that error instead of the data shape
  4. 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

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


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