BerriAI/litellm · warning · ProxyException

Invalid key_alias

Error message

Invalid key_alias

What it means

Key aliases are validated with the same guard LiteLLM uses for secret-manager names (raise_if_unsafe_secret_name) before any format checks. The guard rejects '..' appearing as a whole path segment ('../x', 'x/..', or exactly '..') and any ASCII control characters (\x00-\x1f, \x7f-\x9f, which includes tabs and newlines), because aliases can flow into secret-manager lookups. A violation becomes a 400 ProxyException with param='key_alias' and the terse message 'Invalid key_alias'.

Source

Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:6628

    The remaining charset/length rules are gated behind
    ``litellm.enable_key_alias_format_validation`` (default **False**). When disabled,
    only the baseline validation above is performed, so existing workflows are not
    broken.

    Rules (when enabled):
    - None is OK (no alias).
    - Otherwise must be 2–255 chars
    - start/end with alphanumeric
    - only allow a-zA-Z0-9_-/.@
    """
    if key_alias is None:
        return

    try:
        raise_if_unsafe_secret_name(key_alias)
    except ValueError:
        raise ProxyException(
            message="Invalid key_alias",
            type=ProxyErrorTypes.bad_request_error,
            param="key_alias",
            code=400,
        )

    if not litellm.enable_key_alias_format_validation:
        return

    if not _KEY_ALIAS_PATTERN.match(key_alias):
        raise ProxyException(
            message="Invalid key_alias format. Must be 2-255 characters, start/end with alphanumeric, and only contain a-zA-Z0-9_-/.@.",
            type=ProxyErrorTypes.bad_request_error,
            param="key_alias",
            code=400,
        )

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Remove '..' segments and any '/'-path traversal patterns from the alias
  2. Sanitize the string: strip control characters (alias = ''.join(c for c in alias if c >= ' ' and c != chr(127))) or just c.strip() plus a printable-check
  3. Prefer flat slugs like 'team-prod-readonly' over path-shaped names
  4. Note this check always runs — it is not gated by litellm.enable_key_alias_format_validation

Example fix

# before
await client.post('/key/generate', json={'key_alias': '../secrets/prod'})   # 400: Invalid key_alias
# after
await client.post('/key/generate', json={'key_alias': 'prod-readonly-alias'})
Defensive patterns

Strategy: validation

Validate before calling

import re

_UNSAFE_SECRET_NAME = re.compile(r'(^|/)\.\.(/|$)|[\x00-\x1f\x7f-\x9f]')

def sanitize_alias(alias: str) -> str:
    cleaned = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', alias)
    cleaned = '/'.join(seg for seg in cleaned.split('/') if seg != '..')
    return cleaned

Type guard

def is_safe_alias(alias: str) -> bool:
    return bool(alias) and _UNSAFE_SECRET_NAME.search(alias) is None

Try / catch

try:
    await client.post('/key/generate', json={'key_alias': alias, **rest})
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400 and 'key_alias' in e.response.text:
        await client.post('/key/generate', json={'key_alias': sanitize_alias(alias), **rest})
    else:
        raise

Prevention

When it happens

Trigger: POST /key/generate or /key/update with key_alias='../secrets/openai', 'team/../prod', '..', or an alias pasted from a terminal that embedded a tab/newline (e.g. 'prod\talias').

Common situations: Aliases derived from file paths or branch names; YAML/JSON config files where the alias string accidentally spans lines; CI variables containing trailing carriage returns on Windows.

Related errors


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