{"record":{"id":"a207ed95423047db","repo":"BerriAI/litellm","slug":"key-with-alias-key-alias-already-exists-uniqu","errorCode":null,"errorMessage":"Key with alias '{key_alias}' already exists. Unique key aliases across all keys are required.","messagePattern":"Key with alias '(.+?)' already exists\\. Unique key aliases across all keys are required\\.","errorType":"http","errorClass":"ProxyException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/management_endpoints/key_management_endpoints.py","lineNumber":6672,"sourceCode":"\n    Args:\n        key_alias (Optional[str]): The key alias to check\n        prisma_client (Any): Prisma client instance\n        existing_key_token (Optional[str]): ID of existing key being updated, to exclude from uniqueness check\n            (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)\n\n    Raises:\n        ProxyException: If key alias already exists on a different key\n    \"\"\"\n    if key_alias is not None and prisma_client is not None:\n        where_clause: Final[dict[str, object]] = {\"key_alias\": key_alias}\n        if existing_key_token:\n            # Exclude the current key from the uniqueness check\n            where_clause[\"NOT\"] = {\"token\": existing_key_token}\n\n        existing_key = await _prisma_table(VerificationTokenRepository(prisma_client)).find_first(where=where_clause)\n        if existing_key is not None:\n            raise ProxyException(\n                message=f\"Key with alias '{key_alias}' already exists. Unique key aliases across all keys are required.\",\n                type=ProxyErrorTypes.bad_request_error,\n                param=\"key_alias\",\n                code=status.HTTP_400_BAD_REQUEST,\n            )\n\n\ndef validate_model_max_budget(model_max_budget: dict | None) -> None:\n    \"\"\"\n    Validate the model_max_budget is GenericBudgetConfigType + enforce user has an enterprise license\n\n    Raises:\n        Exception: If model_max_budget is not a valid GenericBudgetConfigType\n    \"\"\"\n    try:\n        if model_max_budget is None:\n            return\n        if len(model_max_budget) == 0:","sourceCodeStart":6654,"sourceCodeEnd":6690,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/key_management_endpoints.py#L6654-L6690","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nawait client.post('/key/generate', json={'key_alias': 'prod'})   # 400: Key with alias 'prod' already exists...\n# after\nexisting = {k['key_alias'] for k in (await client.get('/key/list')).json()['keys']}\nalias = 'prod' if 'prod' not in existing else f'prod-{uuid4().hex[:6]}'\nawait client.post('/key/generate', json={'key_alias': alias})","handlingStrategy":"try-catch","validationCode":"async def unique_alias(client: httpx.AsyncClient, desired: str) -> str:\n    r = await client.get('/key/list')\n    taken = {k.get('key_alias') for k in r.json().get('keys', [])}\n    if desired not in taken:\n        return desired\n    i = 2\n    while f'{desired}-{i}' in taken:\n        i += 1\n    return f'{desired}-{i}'","typeGuard":null,"tryCatchPattern":"try:\n    r = await client.post('/key/generate', json={'key_alias': alias, **rest})\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 400 and 'already exists' in e.response.text:\n        alias = f'{alias}-{uuid4().hex[:6]}'\n        r = await client.post('/key/generate', json={'key_alias': alias, **rest})\n    else:\n        raise","preventionTips":["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"],"tags":["key-alias","uniqueness","conflict","litellm-proxy","keys"],"backgroundTag":"unique-constraint-violation","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}