BerriAI/litellm · error · HTTPException
Admin-only endpoint. Not allowed to access this., your role=
Error message
Admin-only endpoint. Not allowed to access this., your role={user_api_key_dict.user_role} What it means
GET /budget/info exposes every tenant's budget configuration, so after the DB check it enforces an admin-view gate via user_api_key_has_admin_view: the calling key's user_role must be LitellmUserRoles.PROXY_ADMIN or LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY. Any other role (internal_user, team admin, unknown) gets HTTP 400 with CommonProxyErrors.not_allowed_access plus the caller's actual role echoed in the message. The role comes from the UserAPIKeyAuth object resolved during user_api_key_auth, i.e. from the virtual key and its bound user row.
Source
Thrown at litellm/proxy/management_endpoints/budget_management_endpoints.py:248
):
"""
Get list of configurable params + current value for a budget item + description of each field
Used on Admin UI.
Query Parameters:
- budget_id: str - The budget id to get information for
"""
from litellm.proxy.proxy_server import prisma_client
if prisma_client is None:
raise HTTPException(
status_code=400,
detail={"error": CommonProxyErrors.db_not_connected_error.value},
)
if not _user_has_admin_view(user_api_key_dict):
raise HTTPException(
status_code=400,
detail={"error": f"{CommonProxyErrors.not_allowed_access.value}, your role={user_api_key_dict.user_role}"},
)
## get budget item from db
db_budget_row: Final = await BudgetRepository(prisma_client).table.find_first(where={"budget_id": budget_id})
if db_budget_row is not None:
db_budget_row_dict = db_budget_row.model_dump(exclude_none=True)
else:
db_budget_row_dict = {}
allowed_args: Final = {
"max_parallel_requests": {"type": "Integer"},
"tpm_limit": {"type": "Integer"},
"rpm_limit": {"type": "Integer"},
"budget_duration": {"type": "String"},
"max_budget": {"type": "Float"},View on GitHub (pinned to 77b7c6c40c)
Solutions
- Re-run the call with the proxy master key (its role resolves to PROXY_ADMIN)
- Or elevate the key's owner: update the user with role=proxy_admin via /user/update, then use a key bound to that user
- For read-only dashboard use, a PROXY_ADMIN_VIEW_ONLY key also passes the admin-view check
Example fix
# before: user-scoped key -> 400 Admin-only endpoint curl http://localhost:4000/budget/info?budget_id=b1 -H "Authorization: Bearer sk-team-key" # after: master key or proxy_admin-owned key curl http://localhost:4000/budget/info?budget_id=b1 -H "Authorization: Bearer $LITELLM_MASTER_KEY"
Defensive patterns
Strategy: validation
Validate before calling
import httpx
def caller_role(proxy_url: str, key: str) -> str:
# /user/info resolves the role of the user bound to this key
r = httpx.get(f"{proxy_url}/user/info", headers={"Authorization": f"Bearer {key}"})
r.raise_for_status()
return r.json().get("user_info", {}).get("user_role", "unknown")
ADMIN_VIEW_ROLES = {"proxy_admin", "proxy_admin_viewer"}
assert caller_role(PROXY_URL, KEY) in ADMIN_VIEW_ROLES, "need admin view for /budget/info" Type guard
from typing import TypeGuard
ADMIN_VIEW_ROLES = {"proxy_admin", "proxy_admin_viewer"}
def has_admin_view(role: object) -> TypeGuard[str]:
"""True when the resolved LiteLLM role may call admin-view endpoints."""
return isinstance(role, str) and role in ADMIN_VIEW_ROLES Try / catch
import httpx
try:
r = httpx.get(f"{PROXY_URL}/budget/info", params={"budget_id": bid}, headers=hdrs)
r.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 400 and "Admin-only endpoint" in e.response.text:
# role echoed in detail['error']; switch to an admin key or elevate the user
raise PermissionError(e.response.json()["detail"]["error"]) from e
raise Prevention
- Keep a dedicated admin key for management API calls
- Check the bound user's role via /user/info before scripted admin calls
- Never reuse team/internal keys for admin endpoint automation
When it happens
Trigger: Calling GET /budget/info with a virtual key whose bound user has role internal_user or team-only permissions; a script or Admin UI session authenticated with a non-admin personal key instead of the master key.
Common situations: Automation/CI that reuses a default team key against admin endpoints; a dashboard user logged in with org-member credentials; a key minted from a user whose LiteLLM_UserTable role was never elevated to 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
- Unable to record skill ownership: caller has no identity sco
- Skill not found: {skill_id}
- User does not have permission to test MCP server connections
- User does not have permission to test MCP server tools. Only
- spend must be a finite number. Received: {spend}
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/07df76fa19d22ce0.
Report an issue: GitHub.