{"record":{"id":"07df76fa19d22ce0","repo":"BerriAI/litellm","slug":"admin-only-endpoint-not-allowed-to-access-this-07df76","errorCode":null,"errorMessage":"Admin-only endpoint. Not allowed to access this., your role={user_api_key_dict.user_role}","messagePattern":"Admin-only endpoint\\. Not allowed to access this\\., your role=(.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/management_endpoints/budget_management_endpoints.py","lineNumber":248,"sourceCode":"):\n    \"\"\"\n    Get list of configurable params + current value for a budget item + description of each field\n\n    Used on Admin UI.\n\n    Query Parameters:\n    - budget_id: str - The budget id to get information for\n    \"\"\"\n    from litellm.proxy.proxy_server import prisma_client\n\n    if prisma_client is None:\n        raise HTTPException(\n            status_code=400,\n            detail={\"error\": CommonProxyErrors.db_not_connected_error.value},\n        )\n\n    if not _user_has_admin_view(user_api_key_dict):\n        raise HTTPException(\n            status_code=400,\n            detail={\"error\": f\"{CommonProxyErrors.not_allowed_access.value}, your role={user_api_key_dict.user_role}\"},\n        )\n\n    ## get budget item from db\n    db_budget_row: Final = await BudgetRepository(prisma_client).table.find_first(where={\"budget_id\": budget_id})\n\n    if db_budget_row is not None:\n        db_budget_row_dict = db_budget_row.model_dump(exclude_none=True)\n    else:\n        db_budget_row_dict = {}\n\n    allowed_args: Final = {\n        \"max_parallel_requests\": {\"type\": \"Integer\"},\n        \"tpm_limit\": {\"type\": \"Integer\"},\n        \"rpm_limit\": {\"type\": \"Integer\"},\n        \"budget_duration\": {\"type\": \"String\"},\n        \"max_budget\": {\"type\": \"Float\"},","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/budget_management_endpoints.py#L230-L266","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: user-scoped key -> 400 Admin-only endpoint\ncurl http://localhost:4000/budget/info?budget_id=b1 -H \"Authorization: Bearer sk-team-key\"\n\n# after: master key or proxy_admin-owned key\ncurl http://localhost:4000/budget/info?budget_id=b1 -H \"Authorization: Bearer $LITELLM_MASTER_KEY\"","handlingStrategy":"validation","validationCode":"import httpx\n\ndef caller_role(proxy_url: str, key: str) -> str:\n    # /user/info resolves the role of the user bound to this key\n    r = httpx.get(f\"{proxy_url}/user/info\", headers={\"Authorization\": f\"Bearer {key}\"})\n    r.raise_for_status()\n    return r.json().get(\"user_info\", {}).get(\"user_role\", \"unknown\")\n\nADMIN_VIEW_ROLES = {\"proxy_admin\", \"proxy_admin_viewer\"}\nassert caller_role(PROXY_URL, KEY) in ADMIN_VIEW_ROLES, \"need admin view for /budget/info\"","typeGuard":"from typing import TypeGuard\n\nADMIN_VIEW_ROLES = {\"proxy_admin\", \"proxy_admin_viewer\"}\n\ndef has_admin_view(role: object) -> TypeGuard[str]:\n    \"\"\"True when the resolved LiteLLM role may call admin-view endpoints.\"\"\"\n    return isinstance(role, str) and role in ADMIN_VIEW_ROLES","tryCatchPattern":"import httpx\n\ntry:\n    r = httpx.get(f\"{PROXY_URL}/budget/info\", params={\"budget_id\": bid}, headers=hdrs)\n    r.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 400 and \"Admin-only endpoint\" in e.response.text:\n        # role echoed in detail['error']; switch to an admin key or elevate the user\n        raise PermissionError(e.response.json()[\"detail\"][\"error\"]) from e\n    raise","preventionTips":["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"],"tags":["litellm-proxy","authorization","admin-only","roles","budget-management"],"backgroundTag":"insufficient-permissions","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}