BerriAI/litellm · error · HTTPException
f"Only proxy admins can create administrative users (proxy_a
Error message
f"Only proxy admins can create administrative users (proxy_admin, proxy_admin_viewer). Attempted to create user with role: {data.user_role}. Your role: {user_api_key_dict.user_role}" What it means
POST /user/new refuses to create administrative users unless the caller is a proxy admin. If the request body sets user_role to proxy_admin or proxy_admin_viewer while the authenticated key's role (UserAPIKeyAuth.user_role) is not proxy_admin, LiteLLM returns 403. The guard blocks privilege escalation: team admins and internal users must not be able to mint new admins. (The isinstance(user_api_key_dict, UserAPIKeyAuth) condition only relaxes the check for direct test calls that pass a Depends object.)
Source
Thrown at litellm/proxy/management_endpoints/internal_user_endpoints.py:542
await _check_duplicate_user_email(data.user_email, prisma_client)
# Check if license is over limit
billable_users: Final = await UserRepository(prisma_client).count_billable_users()
if billable_users and _license_check.is_over_limit(total_users=billable_users):
raise HTTPException(
status_code=403,
detail="License is over limit. Please contact support@berri.ai to upgrade your license.",
)
# Only proxy admins can create administrative users
# Check if user_api_key_dict is actually a UserAPIKeyAuth instance (not a Depends object)
# This can happen when the function is called directly in tests
if (
data.user_role in [LitellmUserRoles.PROXY_ADMIN, LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY]
and isinstance(user_api_key_dict, UserAPIKeyAuth)
and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN
):
raise HTTPException(
status_code=403,
detail=f"Only proxy admins can create administrative users (proxy_admin, proxy_admin_viewer). Attempted to create user with role: {data.user_role}. Your role: {user_api_key_dict.user_role}",
)
_check_permissions_caller_permission(
data=data,
user_api_key_dict=user_api_key_dict,
)
data_json = data.json()
data_json = _update_internal_new_user_params(data_json, data)
# Persist the requested grants as their own row and link it, mirroring key/team creation.
# generate_key_helper_fn only forwards object_permission_id, so without this the entitlement
# the caller sent would be dropped on the floor.
data_json = await _set_object_permission(data_json=data_json, prisma_client=prisma_client)
_hash_password_in_dict(data_json)
teams = data.teams
if teams is None:View on GitHub (pinned to 77b7c6c40c)
Solutions
- Authenticate with a proxy_admin key (the master key or a key bound to an admin user) for the create call
- Create the user without user_role (defaults to internal_user) and have a proxy admin promote them afterwards via POST /user/update
- Drop or change user_role in the payload if an administrative user was not intended
Example fix
# before: team-admin key -> 403
curl -X POST http://localhost:4000/user/new -H 'Authorization: Bearer sk-team-admin' -d '{"user_id": "u1", "user_role": "proxy_admin"}'
# after: proxy-admin key
curl -X POST http://localhost:4000/user/new -H 'Authorization: Bearer sk-master-key' -d '{"user_id": "u1", "user_role": "proxy_admin"}' # 200 Defensive patterns
Strategy: validation
Validate before calling
import requests
def assert_can_create_admins(base_url: str, key: str) -> None:
r = requests.get(f"{base_url}/key/info", params={"key": key}, timeout=10)
r.raise_for_status()
info = r.json()["info"]
role = info.get("user_role") or "internal_user"
if role != "proxy_admin":
raise PermissionError(f"key role is {role}; proxy_admin required to create admin users") Try / catch
except requests.HTTPError as e:
if e.response is not None and e.response.status_code == 403 and "Only proxy admins" in e.response.text:
# retry with an admin key, or re-submit without user_role
... Prevention
- Run admin-provisioning calls only with the master/admin key, never team or user keys
- Keep user_role out of generic onboarding payloads
- Assert the caller's role via /key/info before any privileged mutation
When it happens
Trigger: POST /user/new with body containing "user_role": "proxy_admin" or "proxy_admin_viewer", authenticated with a virtual key whose user is internal_user or team_admin; provisioning scripts that try to create admins with a non-admin service key.
Common situations: Automation run with team-admin keys; copy-pasted payloads from docs that include user_role; upgrading LiteLLM to a version where this escalation check was added and previously-working calls now fail with 403.
Related errors
- Only proxy admins can modify user roles.
- User does not have permission to update this user. Only PROX
- f"Non-admin users cannot modify '{_field}' on their own reco
- Only admins or team admins can create projects. Your role is
- Only admins or team admins can update projects
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/1e8b04f5a18a8dc5.
Report an issue: GitHub.