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
- Call with the master key (LITELLM_MASTER_KEY) or a key whose user has the proxy_admin role
- An admin can elevate the user via /user/update (role: proxy_admin), or issue a proxy_admin_view_only key for read-only access
- 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
- Store admin and non-admin keys separately in secret managers and label them clearly
- Check the key's user_role via /key/info before scripting admin endpoints
- Grant proxy_admin_view_only for reporting tooling that only needs to read
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.