BerriAI/litellm · error · ProxyException
Key with alias '{key_alias}' already exists. Unique key alia
Error message
Key with alias '{key_alias}' already exists. Unique key aliases across all keys are required. What it means
LiteLLM enforces that key_alias is unique across ALL keys (a global unique constraint implemented with a find_first query, not just a DB index). When creating or updating a key with an alias already owned by a different token, _enforce_unique_key_alias raises this 400 ProxyException. On updates the current key's own token is excluded from the check, so re-saving a key with its existing alias is allowed.
Source
Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:6672
Args:
key_alias (Optional[str]): The key alias to check
prisma_client (Any): Prisma client instance
existing_key_token (Optional[str]): ID of existing key being updated, to exclude from uniqueness check
(The Admin UI passes key_alias, in all Edit key requests. So we need to be sure that if we find a key with the same alias, it's not the same key we're updating)
Raises:
ProxyException: If key alias already exists on a different key
"""
if key_alias is not None and prisma_client is not None:
where_clause: Final[dict[str, object]] = {"key_alias": key_alias}
if existing_key_token:
# Exclude the current key from the uniqueness check
where_clause["NOT"] = {"token": existing_key_token}
existing_key = await _prisma_table(VerificationTokenRepository(prisma_client)).find_first(where=where_clause)
if existing_key is not None:
raise ProxyException(
message=f"Key with alias '{key_alias}' already exists. Unique key aliases across all keys are required.",
type=ProxyErrorTypes.bad_request_error,
param="key_alias",
code=status.HTTP_400_BAD_REQUEST,
)
def validate_model_max_budget(model_max_budget: dict | None) -> None:
"""
Validate the model_max_budget is GenericBudgetConfigType + enforce user has an enterprise license
Raises:
Exception: If model_max_budget is not a valid GenericBudgetConfigType
"""
try:
if model_max_budget is None:
return
if len(model_max_budget) == 0:View on GitHub (pinned to 77b7c6c40c)
Solutions
- Make aliases unique by construction, e.g. f'{team}-{user_id}-{purpose}' instead of a fixed label
- Before creating, check availability: GET /key/list and filter for key_alias, then pick a suffixed variant like 'prod-2'
- On collisions from a retry loop, catch this 400 and generate a fresh alias rather than failing the whole job
- When renaming, remember the old alias frees up only after the update commits
Example fix
# before
await client.post('/key/generate', json={'key_alias': 'prod'}) # 400: Key with alias 'prod' already exists...
# after
existing = {k['key_alias'] for k in (await client.get('/key/list')).json()['keys']}
alias = 'prod' if 'prod' not in existing else f'prod-{uuid4().hex[:6]}'
await client.post('/key/generate', json={'key_alias': alias}) Defensive patterns
Strategy: try-catch
Validate before calling
async def unique_alias(client: httpx.AsyncClient, desired: str) -> str:
r = await client.get('/key/list')
taken = {k.get('key_alias') for k in r.json().get('keys', [])}
if desired not in taken:
return desired
i = 2
while f'{desired}-{i}' in taken:
i += 1
return f'{desired}-{i}' Try / catch
try:
r = await client.post('/key/generate', json={'key_alias': alias, **rest})
except httpx.HTTPStatusError as e:
if e.response.status_code == 400 and 'already exists' in e.response.text:
alias = f'{alias}-{uuid4().hex[:6]}'
r = await client.post('/key/generate', json={'key_alias': alias, **rest})
else:
raise Prevention
- Build aliases from inherently unique parts (user_id, purpose, date) instead of shared labels
- Treat alias creation as a retry-with-new-suffix loop, since a pre-check cannot close the race window
- On key update, keep the alias unchanged unless renaming is intentional
When it happens
Trigger: POST /key/generate with key_alias='prod' when any other key already uses 'prod'; PUT-style key update that changes the alias to one owned by a different key; bulk import scripts that reuse human-readable names per user.
Common situations: Naming aliases after teams/models so collisions are inevitable at scale; retrying a failed create after the first attempt actually succeeded; onboarding flows generating 'user-api-key' for every new user.
Related errors
- Only proxy admins can set `allowed_passthrough_routes` on a
- Invalid sort order. Must be 'asc' or 'desc'
- Key not found: {hashed_token}
- Only proxy admins, team admins, or org admins can call {rout
- Invalid key format.
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/a207ed95423047db.
Report an issue: GitHub.