BerriAI/litellm · error · HTTPException
Service-account keys cannot query user analytics. Use a user
Error message
Service-account keys cannot query user analytics. Use a user-bound key, or call as a proxy admin.
What it means
require_caller_user_id_for_non_admin backs the non-admin branch of the user analytics endpoints: those endpoints scope queries to the caller's own user_id, but service-account keys are deliberately created with user_id=None (key_management_endpoints.py forces data.user_id=None). A None user_id would flow into the daily-activity builder, where entity_id=None means 'no filter' — returning every tenant's data. The guard closes that hole by raising HTTP 403 before the query whenever the non-admin caller has no user_id.
Source
Thrown at litellm/proxy/management_endpoints/common_utils.py:97
def require_caller_user_id_for_non_admin(
user_api_key_dict: UserAPIKeyAuth,
) -> str:
"""Return the caller's user_id, or raise 403 if missing.
Non-admin analytics endpoints scope queries by the caller's own user_id.
Service-account keys are deliberately created with user_id=None
(key_management_endpoints.py forces ``data.user_id = None`` at key
creation). Without this guard, that None value flows through to the
daily-activity builder, which treats ``entity_id is None`` as "no filter"
and returns every tenant's data.
Callers must check is_admin first; this helper is only valid on the
non-admin scoping branch.
"""
if user_api_key_dict.user_id is None:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail={
"error": (
"Service-account keys cannot query user analytics. Use a user-bound key, or call as a proxy admin."
)
},
)
return user_api_key_dict.user_id
def _check_passthrough_routes_caller_permission(
data: BaseModel,
user_api_key_dict: UserAPIKeyAuth,
*,
entity: str = "key",
) -> None:
"""
Only proxy admins may set `allowed_passthrough_routes` (top-level or underView on GitHub (pinned to 77b7c6c40c)
Solutions
- Use a key bound to a real user (create the key with a valid user_id) when self-scoped analytics are needed
- Or perform the call with an admin/master key, which takes the admin branch and is not subject to this guard
- If you own the service account, treat analytics as admin-only and query them from an admin context
Example fix
# before: service-account key (user_id=None) curl http://localhost:4000/user/info -H "Authorization: Bearer sk-service" # 403 Service-account keys cannot query user analytics # after: user-bound key or master key curl http://localhost:4000/user/info -H "Authorization: Bearer sk-user-bound" curl http://localhost:4000/user/info -H "Authorization: Bearer $LITELLM_MASTER_KEY"
Defensive patterns
Strategy: validation
Validate before calling
import httpx
r = httpx.get(f"{PROXY_URL}/key/info", params={"key": KEY}, headers=admin_hdrs)
r.raise_for_status()
info = r.json().get("key_info", {})
if info.get("user_id") is None:
raise PermissionError(
"this is a service-account key; query analytics with a user-bound or admin key"
) Type guard
from typing import TypeGuard
def is_user_bound_key(info: dict) -> TypeGuard[dict]:
"""True when the key's info binds to a real user (analytics-safe)."""
return isinstance(info.get("user_id"), str) and info["user_id"] != "" Try / catch
import httpx
try:
r = httpx.get(f"{PROXY_URL}/user/info", params=analytics_params, headers=hdrs)
r.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 403 and "Service-account keys" in e.response.text:
# correct behavior in multi-tenant setups: switch keys, do not retry
raise PermissionError("use a user-bound key or call as proxy admin") from e
raise Prevention
- Never wire service-account keys to self-scoped analytics endpoints
- For machine analytics, use an admin key and explicit user_id filters
- Treat this 403 as tenant isolation working, not as a bug to bypass
When it happens
Trigger: Calling user analytics endpoints (e.g. GET /user/info spend/activity routes) with a service-account virtual key (created without a user, or explicitly with NO user binding) while not being a proxy admin.
Common situations: Machine-to-machine integrations using service keys that try to read 'their own' analytics; health checks wired to analytics endpoints with a service key; multi-tenant deployments where the 403 is the correct privacy guarantee.
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
- Skill not found: {skill_id}
- Unable to record skill ownership: caller has no identity sco
- Gemini managed-agent endpoints require a caller-supplied Gem
- Admin-only endpoint. Not allowed to access this., your role=
- DB not connected. This endpoint needs a database; set DATABA
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/3f8208510fc0bd56.
Report an issue: GitHub.