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

  1. Make aliases unique by construction, e.g. f'{team}-{user_id}-{purpose}' instead of a fixed label
  2. Before creating, check availability: GET /key/list and filter for key_alias, then pick a suffixed variant like 'prod-2'
  3. On collisions from a retry loop, catch this 400 and generate a fresh alias rather than failing the whole job
  4. 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

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


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/a207ed95423047db. Report an issue: GitHub.