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

  1. Re-run the call with the proxy master key (its role resolves to PROXY_ADMIN)
  2. Or elevate the key's owner: update the user with role=proxy_admin via /user/update, then use a key bound to that user
  3. 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

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


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