BerriAI/litellm · error · HTTPException

Admin-only endpoint. Your user role={user_api_key_dict.user_

Error message

Admin-only endpoint. Your user role={user_api_key_dict.user_role}

What it means

GET /customer/list is restricted to PROXY_ADMIN and PROXY_ADMIN_VIEW_ONLY roles taken from the authenticated key's user (user_api_key_dict.user_role). Any other role (internal_user, team member, etc.) gets HTTP 401 with the caller's actual role echoed in the message. Note the DB check on this route comes after the role check.

Source

Thrown at litellm/proxy/management_endpoints/customer_endpoints.py:844

) -> list[CustomerResponse]:
    """
    [Admin-only] List all available customers

    Example curl:
    ```
    curl --location --request GET 'http://0.0.0.0:4000/customer/list' \
        --header 'Authorization: Bearer sk-1234'
    ```

    """
    try:
        from litellm.proxy.proxy_server import prisma_client

        if (
            user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN
            and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY
        ):
            raise HTTPException(
                status_code=401,
                detail={"error": f"Admin-only endpoint. Your user role={user_api_key_dict.user_role}"},
            )

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

        response: Final = await _typed_table(EndUserRepository(prisma_client)).find_many(
            include={"litellm_budget_table": True, "object_permission": True}
        )

        return [_to_customer_response(item) for item in response]

    except Exception as e:
        verbose_proxy_logger.exception(

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Call with the master key (LITELLM_MASTER_KEY) or a key whose user has the proxy_admin role
  2. An admin can elevate the user via /user/update (role: proxy_admin), or issue a proxy_admin_view_only key for read-only access
  3. Non-admin callers should use scoped endpoints (e.g. GET /customer/info) instead of the full listing

Example fix

# before
headers = {"Authorization": "Bearer sk-team-key"}   # internal_user role -> 401

# after
headers = {"Authorization": "Bearer sk-master-key"}    # proxy_admin -> 200
Defensive patterns

Strategy: validation

Validate before calling

import httpx


def is_admin_key(base: str, headers: dict) -> bool:
    key = headers["Authorization"].split(" ", 1)[1]
    info = httpx.get(f"{base}/key/info", params={"key": key}, headers=headers).json()
    role = (info.get("key_info") or {}).get("user_role")
    return role in ("proxy_admin", "proxy_admin_view_only")

Try / catch

try:
    r = httpx.get(f"{base}/customer/list", headers=headers)
except httpx.HTTPStatusError as e:
    if e.response.status_code == 401 and "Admin-only endpoint" in e.response.text:
        raise PermissionError("/customer/list needs a proxy_admin key") from e
    raise

Prevention

When it happens

Trigger: Calling GET /customer/list with a virtual key whose owning user is not a proxy admin - team-scoped keys, personal keys, or org-member keys created via /user/new.

Common situations: Operators holding team keys try to enumerate all customers; scripts pull the wrong secret from a vault; new staff have the default internal_user role and attempt admin operations on day one.

Related errors


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