BerriAI/litellm · error · HTTPException
project_id is required
Error message
project_id is required
What it means
Update requires the client to name which project to change. Unlike create (which can auto-generate an id), update has no default: a payload where project_id is None — missing, null, or empty — is rejected with 400 immediately after the DB check, before any row is fetched.
Solutions
- Include project_id in the update payload — copy it from GET /project/list or the create response
- If using a serialization layer, disable exclude_none for this call so set ids are not dropped
- Log the outgoing payload at DEBUG before sending to catch silently-dropped required fields
Example fix
# before (400: project_id is required)
httpx.post(base + '/project/update', headers=hdr, json={'project_name': 'new-name'})
# after
httpx.post(base + '/project/update', headers=hdr, json={'project_id': 'p1', 'project_name': 'new-name'}) Defensive patterns
Strategy: validation
Validate before calling
def validate_update_payload(payload: dict) -> None:
pid = payload.get('project_id')
if not isinstance(pid, str) or not pid.strip():
raise ValueError('/project/update requires a non-empty string project_id')
validate_update_payload(payload)
httpx.post(base + '/project/update', headers=hdr, json=payload) Type guard
def is_valid_update_payload(p: dict) -> bool:
return isinstance(p, dict) and isinstance(p.get('project_id'), str) and len(p['project_id'].strip()) > 0 Try / catch
except httpx.HTTPStatusError as e:
if e.response.status_code == 400 and 'project_id is required' in e.response.text:
raise ValueError('update payload dropped project_id; check serialization (exclude_none)') from None Prevention
- Always fetch the project id from create/list responses before composing updates
- Beware serializers configured with exclude_none=True dropping required fields
- Unit-test request payloads against the endpoint schema in CI
When it happens
Trigger: POST /project/update with project_id absent or explicitly null in the JSON body.
Common situations: Client models serialized with exclude_none=True so the field never reaches the wire; copy-pasting a create payload into an update call; UIs that keep a blank 'current project' field.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- max_budget cannot be negative. Received
- Model ' ' not in team's allowed models. Team allowed…
- Project max_budget ( ) exceeds team's max_budget ( ). Team
- Project rpm_limit ( ) exceeds team's rpm_limit ( )
- Project tpm_limit ( ) exceeds team's tpm_limit ( )
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/d8877341ca4afbcd.
Report an issue: GitHub.
Appendix: source
Thrown at enterprise/litellm_enterprise/proxy/management_endpoints/project_endpoints.py:581
# ADD METADATA FIELDS
for field in LiteLLM_ManagementEndpoint_MetadataFields_Premium:
if getattr(data, field, None) is not None:
_set_object_metadata_field(
object_data=data,
field_name=field,
value=getattr(data, field),
)
delattr(data, field)
if prisma_client is None:
raise HTTPException(
status_code=500,
detail={"error": CommonProxyErrors.db_not_connected_error.value},
)
if data.project_id is None:
raise HTTPException(
status_code=400,
detail={"error": "project_id is required"},
)
# Fetch existing project
existing_project: (
prisma_models.LiteLLM_ProjectTable | None
) = await _project_table(prisma_client).find_unique(where={"project_id": data.project_id})
if existing_project is None:
raise ProxyException(
message=f"Project not found, project_id={data.project_id}",
type="not_found",
code=404,
param="project_id",
)
# Permission to *edit* the project must be evaluated against theView on GitHub (pinned to 77b7c6c40c)