BerriAI/litellm · error · HTTPException
Only admins or team admins can create projects. Your role is
Error message
Only admins or team admins can create projects. Your role is {user_api_key_dict.user_role} What it means
Project creation is restricted to proxy admins or admins of the target team. _check_user_permission_for_project resolves the caller from the virtual key and passes only for proxy-admin roles or users listed with an admin role in the team's members_with_roles. On failure the endpoint returns 403 and echoes the caller's user_role so you can see exactly why the check failed.
Solutions
- Retry with the proxy admin master key
- Add the caller's user_id to the team as an admin first (POST /team/update with members_with_roles containing an admin entry for that user), then retry
- Inspect the role echoed in the message: internal_user/app_user means the key belongs to a non-admin account — issue the key from an admin user instead
- Double-check data.team_id: being admin of team A grants no rights to create projects under team B
Example fix
# before (403 with team-member key)
httpx.post(base + '/project/new', headers=team_member_hdr, json={'team_id': 'team-a', 'project_name': 'billing'})
# after: make the user an admin of the team, then create
httpx.post(base + '/team/update', headers=admin_hdr, json={'team_id': 'team-a', 'members_with_roles': [{'role': 'admin', 'user_id': 'user-123'}]})
httpx.post(base + '/project/new', headers=user_123_hdr, json={'team_id': 'team-a', 'project_name': 'billing'}) Defensive patterns
Strategy: try-catch
Try / catch
try:
r = httpx.post(base + '/project/new', headers=hdr, json=payload)
r.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 403 and 'Only admins or team admins' in e.response.text:
role = extract_role(e.response.text) # message echoes user_role
raise PermissionError(f'caller role {role} cannot create projects for team {payload["team_id"]}; use an admin of that team') from None
raise Prevention
- Drive project provisioning with the proxy admin key
- Maintain an explicit map of which users hold admin roles in each team (members_with_roles)
- Test permission-dependent automation with a non-admin key before shipping
When it happens
Trigger: POST /project/new where data.team_id names a team whose members_with_roles does not include the key's user as an admin, or where the key maps to a non-admin user (e.g. user_role internal_user or app_user).
Common situations: Automating project setup with a team-member or service key; platform-team-owned teams that other teams try to create projects under; SSO role mapping that assigns internal_user to the caller, which never passes this check.
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
- Only admins or team admins can update projects
- You don't have access to this project
- Cannot reassign project to a team you are not an admin of
- Only admins can delete projects
- 404
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/feb68cbd01a558e4.
Report an issue: GitHub.
Appendix: source
Thrown at enterprise/litellm_enterprise/proxy/management_endpoints/project_endpoints.py:412
team_object = await _validate_team_exists(team_id=data.team_id, prisma_client=prisma_client)
# Validate project limits against team limits
_check_team_project_limits(
team_object=LiteLLM_TeamTable.model_validate(team_object.model_dump()),
data=data,
)
# Check if user has permission to create projects for this team
# only team admins can create projects for their team
has_permission = await _check_user_permission_for_project(
user_api_key_dict=user_api_key_dict,
team_id=data.team_id,
prisma_client=prisma_client,
team_object=LiteLLM_TeamTable.model_validate(team_object.model_dump()),
)
if not has_permission:
raise HTTPException(
status_code=403,
detail={
"error": f"Only admins or team admins can create projects. Your role is {user_api_key_dict.user_role}"
},
)
# Generate project_id if not provided
if data.project_id is None:
data.project_id = str(uuid.uuid4())
else:
# Check if project_id already exists
existing_project = await _project_table(prisma_client).find_unique(where={"project_id": data.project_id})
if existing_project is not None:
raise ProxyException(
message=f"Project id = {data.project_id} already exists. Please use a different project id.",
type="bad_request",
code=400,
param="project_id",View on GitHub (pinned to 77b7c6c40c)