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
- Remove '..' segments and any '/'-path traversal patterns from the alias
- 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
- Prefer flat slugs like 'team-prod-readonly' over path-shaped names
- 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
- Never derive aliases from raw filesystem paths or branch names without sanitizing
- Add a printable-ASCII assertion wherever aliases enter the system (UI forms, YAML, CSV import)
- Remember this traversal check runs even when format validation is disabled
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
- git-subdir 'path' must be a relative path of the form 'segme
- Invalid key_alias format. Must be 2-255 characters, start/en
- mcp_tools_config must be a list of dictionaries
- GitHub source must include 'repo' field (e.g., 'org/repo')
- URL source must include 'url' field (e.g., 'https://github.c
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/5e3e10079f79a37a.
Report an issue: GitHub.