BerriAI/litellm · error · HTTPException

max_budget cannot be negative. Received: {data.max_budget}

Error message

max_budget cannot be negative. Received: {data.max_budget}

What it means

_check_team_project_limits rejects negative budgets before any DB write: NewProjectRequest/UpdateProjectRequest with max_budget < 0 returns HTTP 400 echoing the received value. It mirrors the equivalent team-level checks in team_endpoints.py.

Source

Thrown at enterprise/litellm_enterprise/proxy/management_endpoints/project_endpoints.py:143

    team_object: LiteLLM_TeamTable,
    data: NewProjectRequest | UpdateProjectRequest,
) -> None:
    """
    Check that project limits respect its parent Team's limits.

    Mirrors _check_org_team_limits() from team_endpoints.py.

    Validates:
    - Project models are a subset of Team models
    - Project max_budget <= Team max_budget
    - Project tpm_limit <= Team tpm_limit
    - Project rpm_limit <= Team rpm_limit
    - Budget values are non-negative
    - soft_budget < max_budget
    """
    # --- Budget non-negativity checks ---
    if data.max_budget is not None and data.max_budget < 0:
        raise HTTPException(
            status_code=400,
            detail={"error": f"max_budget cannot be negative. Received: {data.max_budget}"},
        )
    if data.soft_budget is not None and data.soft_budget < 0:
        raise HTTPException(
            status_code=400,
            detail={"error": f"soft_budget cannot be negative. Received: {data.soft_budget}"},
        )

    # --- soft_budget < max_budget ---
    if data.soft_budget is not None and data.max_budget is not None:
        if data.soft_budget >= data.max_budget:
            raise HTTPException(
                status_code=400,
                detail={
                    "error": f"soft_budget ({data.soft_budget}) must be strictly lower than max_budget ({data.max_budget})"
                },
            )

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Omit max_budget entirely (null means unset) or send a non-negative value
  2. Use 0 to hard-block project spend, never a negative
  3. Fix client sentinel logic so unset numerics are omitted, not -1

Example fix

# before
{"team_id": "team-123", "project_alias": "p1", "max_budget": -1}

# after
{"team_id": "team-123", "project_alias": "p1", "max_budget": 100}
Defensive patterns

Strategy: validation

Validate before calling

def sanitize_project_payload(data: dict) -> dict:
    mb = data.get("max_budget")
    if mb is not None and mb < 0:
        raise ValueError(f"max_budget must be >= 0, got {mb}")
    return data

Type guard

def is_valid_budget(v) -> bool:
    return v is None or (isinstance(v, (int, float)) and not isinstance(v, bool) and v >= 0)

Try / catch

try:
    await post("/project/new", json=data)
except HTTPStatusError as e:
    if e.response.status_code == 400 and "cannot be negative" in e.response.text:
        data["max_budget"] = max(0, data["max_budget"])  # or drop the field
        retry()
    raise

Prevention

When it happens

Trigger: POST /project/new or POST /project/update with "max_budget": -1 (or any negative number) in the payload.

Common situations: Clients using -1 as a 'no limit' sentinel; arithmetic like remaining - spent producing negatives; form defaults that initialize unset numerics to -1.

Related errors


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