BerriAI/litellm · error · HTTPException

Only admins can delete projects

Error message

Only admins can delete projects

What it means

Deletion is admin-only: _check_user_permission_for_project is invoked with team_id=None and require_admin=True, which only proxy-admin roles satisfy. Team admins — who may create and update projects — explicitly cannot delete them.

Source

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

                },
            )

        if prisma_client is None:
            raise HTTPException(
                status_code=500,
                detail={"error": CommonProxyErrors.db_not_connected_error.value},
            )

        # Check if user is admin (only admins can delete projects)
        has_permission = await _check_user_permission_for_project(
            user_api_key_dict=user_api_key_dict,
            team_id=None,
            prisma_client=prisma_client,
            require_admin=True,
        )

        if not has_permission:
            raise HTTPException(
                status_code=403,
                detail={"error": "Only admins can delete projects"},
            )

        deleted_projects: list[prisma_models.LiteLLM_ProjectTable | None] = []

        for project_id in data.project_ids:
            # Check if project exists
            existing_project = await _project_table(prisma_client).find_unique(where={"project_id": project_id})

            if existing_project is None:
                raise ProxyException(
                    message=f"Project not found, project_id={project_id}",
                    type="not_found",
                    code=404,
                    param="project_ids",
                )

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Run deletions with the proxy admin master key
  2. Keep the admin credential in a secret manager and restrict which jobs can use it
  3. If teams must self-serve deletion, front it with an internal service holding the admin key plus your own approval flow
  4. Check the key's user role (/user/info or the key record) if you are unsure why it is rejected

Example fix

# before (403: team admin key)
httpx.post(base + '/project/delete', headers=team_admin_hdr, json={'project_ids': ['p1']})

# after: proxy admin master key
httpx.post(base + '/project/delete', headers=proxy_admin_hdr, json={'project_ids': ['p1']})
Defensive patterns

Strategy: try-catch

Try / catch

try:
    r = httpx.post(base + '/project/delete', headers=hdr, json={'project_ids': ids})
    r.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 403 and 'Only admins can delete' in e.response.text:
        raise PermissionError('project deletion is proxy-admin-only; rerun with the admin master key') from None
    raise

Prevention

When it happens

Trigger: POST /project/delete with any non-proxy-admin key: a team admin, internal user, or org member credential.

Common situations: Assuming CRUD permissions are symmetric for team admins; cleanup scripts running with a scoped team key; SSO role mapping that leaves the key's user below proxy_admin.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


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