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

  1. Use a proxy admin key, or an admin of the project's current owning team
  2. Grant the caller admin rights on the owning team and retry
  3. 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

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


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/7050af405c6fa9e8. Report an issue: GitHub.