BerriAI/litellm · error · HTTPException
Only admins or team admins can update projects
Error message
Only admins or team admins can update projects
What it means
PUT /project/update authorizes the caller with _check_user_permission_for_project evaluated against the project's CURRENT team (existing_project.team_id), not the payload's team_id — deliberately, so a team admin cannot hijack a project by supplying their own team_id (VERIA-55). Callers who are neither proxy admins nor admins of the owning team get HTTP 403.
Source
Thrown at enterprise/litellm_enterprise/proxy/management_endpoints/project_endpoints.py:589
)
# Permission to *edit* the project must be evaluated against the
# project's CURRENT team. Sourcing the team from `data.team_id`
# would let an admin of any team pass the check by supplying their
# own team_id, hijacking the project (VERIA-55).
target_team_id = data.team_id or existing_project.team_id
target_team_obj = None
if target_team_id is not None:
target_team_obj = await _validate_team_exists(team_id=target_team_id, prisma_client=prisma_client)
has_permission = await _check_user_permission_for_project(
user_api_key_dict=user_api_key_dict,
team_id=existing_project.team_id,
prisma_client=prisma_client,
)
if not has_permission:
raise HTTPException(
status_code=403,
detail={"error": "Only admins or team admins can update projects"},
)
# Reassigning to a different team also requires admin rights on the
# destination team — otherwise a team admin could shed projects into
# an unsuspecting team's namespace.
if data.team_id is not None and data.team_id != existing_project.team_id:
can_assign_to_target = await _check_user_permission_for_project(
user_api_key_dict=user_api_key_dict,
team_id=data.team_id,
prisma_client=prisma_client,
team_object=(
LiteLLM_TeamTable.model_validate(target_team_obj.model_dump()) if target_team_obj else None
),
)
if not can_assign_to_target:
raise HTTPException(View on GitHub (pinned to 6c2dcb801b)
Solutions
- Use a proxy admin key, or an admin of the project's current owning team
- Grant the caller admin rights on the owning team and retry
- If reassignment is legitimate, first have an admin of the current team (or a proxy admin) perform the update
Example fix
# before (admin of team A, project owned by team B)
curl -H 'Authorization: Bearer sk-teamA-admin' -X PUT .../project/update -d '{"project_id":"p1","team_id":"A"}'
# after (proxy admin or team-B admin performs it)
curl -H 'Authorization: Bearer sk-admin' -X PUT .../project/update -d '{"project_id":"p1",...}' Defensive patterns
Strategy: try-catch
Validate before calling
# Authorization depends on server-side team membership; pre-check what you can:
me = await client.get('/user/info')
if me.user_info.get('user_role') != 'proxy_admin':
raise PermissionError('updates require proxy admin or admin of the project owning team') Try / catch
catch (e) {
if (e.status === 403 && /Only admins or team admins can update/.test(e.body?.detail?.error ?? '')) {
throw new Error('Not authorized for this project team; escalate to a proxy admin or owning-team admin');
}
throw e;
} Prevention
- Resolve the project's owning team (GET /project/info) and match it to the caller's admin scope before updating
- Never trust client-supplied team_id for authorization decisions in your own tooling; mirror the server's team-of-record logic
When it happens
Trigger: Calling PUT /project/update with a non-admin key, or with a team-admin key for team A while the project belongs to team B (even if data.team_id claims team A).
Common situations: Team admins attempting to take over projects by reassigning team_id in the payload, member-level keys used for maintenance automation, or admins operating across teams they do not manage.
Related errors
- Only admins or team admins can create projects. Your role is
- Cannot reassign project to a team you are not an admin of
- An error occurred: {str(e)}, banned_keywords_list={banned_ke
- Model '{m}' not in team's allowed models. Team allowed model
- Project max_budget ({data.max_budget}) exceeds team's max_bu
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/7050af405c6fa9e8.
Report an issue: GitHub.