{"record":{"id":"5aee9827d6fb90c1","repo":"BerriAI/litellm","slug":"max-budget-cannot-be-negative-received-data-max","errorCode":null,"errorMessage":"max_budget cannot be negative. Received: {data.max_budget}","messagePattern":"max_budget cannot be negative\\. Received: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"enterprise/litellm_enterprise/proxy/management_endpoints/project_endpoints.py","lineNumber":143,"sourceCode":"    team_object: LiteLLM_TeamTable,\n    data: NewProjectRequest | UpdateProjectRequest,\n) -> None:\n    \"\"\"\n    Check that project limits respect its parent Team's limits.\n\n    Mirrors _check_org_team_limits() from team_endpoints.py.\n\n    Validates:\n    - Project models are a subset of Team models\n    - Project max_budget <= Team max_budget\n    - Project tpm_limit <= Team tpm_limit\n    - Project rpm_limit <= Team rpm_limit\n    - Budget values are non-negative\n    - soft_budget < max_budget\n    \"\"\"\n    # --- Budget non-negativity checks ---\n    if data.max_budget is not None and data.max_budget < 0:\n        raise HTTPException(\n            status_code=400,\n            detail={\"error\": f\"max_budget cannot be negative. Received: {data.max_budget}\"},\n        )\n    if data.soft_budget is not None and data.soft_budget < 0:\n        raise HTTPException(\n            status_code=400,\n            detail={\"error\": f\"soft_budget cannot be negative. Received: {data.soft_budget}\"},\n        )\n\n    # --- soft_budget < max_budget ---\n    if data.soft_budget is not None and data.max_budget is not None:\n        if data.soft_budget >= data.max_budget:\n            raise HTTPException(\n                status_code=400,\n                detail={\n                    \"error\": f\"soft_budget ({data.soft_budget}) must be strictly lower than max_budget ({data.max_budget})\"\n                },\n            )","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/enterprise/litellm_enterprise/proxy/management_endpoints/project_endpoints.py#L125-L161","documentation":"_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.","triggerScenarios":"POST /project/new or POST /project/update with \"max_budget\": -1 (or any negative number) in the payload.","commonSituations":"Clients using -1 as a 'no limit' sentinel; arithmetic like remaining - spent producing negatives; form defaults that initialize unset numerics to -1.","solutions":["Omit max_budget entirely (null means unset) or send a non-negative value","Use 0 to hard-block project spend, never a negative","Fix client sentinel logic so unset numerics are omitted, not -1"],"exampleFix":"# before\n{\"team_id\": \"team-123\", \"project_alias\": \"p1\", \"max_budget\": -1}\n\n# after\n{\"team_id\": \"team-123\", \"project_alias\": \"p1\", \"max_budget\": 100}","handlingStrategy":"validation","validationCode":"def sanitize_project_payload(data: dict) -> dict:\n    mb = data.get(\"max_budget\")\n    if mb is not None and mb < 0:\n        raise ValueError(f\"max_budget must be >= 0, got {mb}\")\n    return data","typeGuard":"def is_valid_budget(v) -> bool:\n    return v is None or (isinstance(v, (int, float)) and not isinstance(v, bool) and v >= 0)","tryCatchPattern":"try:\n    await post(\"/project/new\", json=data)\nexcept HTTPStatusError as e:\n    if e.response.status_code == 400 and \"cannot be negative\" in e.response.text:\n        data[\"max_budget\"] = max(0, data[\"max_budget\"])  # or drop the field\n        retry()\n    raise","preventionTips":["Validate budgets client-side before sending","Never use -1 as an 'unset' sentinel in LiteLLM payloads — omit the field instead"],"tags":["litellm","projects","budget","validation","http-400"],"backgroundTag":"request-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T10:36:37.832Z"}