BerriAI/litellm · error · ProxyException

either key or key_alias must be provided

Error message

either key or key_alias must be provided

What it means

_get_and_validate_existing_key requires exactly one identifier for the target key: 'token' (the key itself) or 'key_alias'. If the caller supplies neither (empty request body, or only update fields like max_budget), it raises a ProxyException with HTTP 400 'either key or key_alias must be provided'. The request never reaches the database.

Source

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

    if token is not None:
        hashed_token: Final = _hash_token_if_needed(token=token)

        existing_key_row: Final[LiteLLM_VerificationToken | None] = await _prisma_table(
            VerificationTokenRepository(prisma_client)
        ).find_unique(where={"token": hashed_token})

        if existing_key_row is None:
            raise ProxyException(
                message="Key not found.",
                type=ProxyErrorTypes.not_found_error,
                param="key",
                code=status.HTTP_404_NOT_FOUND,
            )

        return existing_key_row

    if key_alias is None:
        raise ProxyException(
            message="either key or key_alias must be provided",
            type=ProxyErrorTypes.bad_request_error,
            param="key",
            code=status.HTTP_400_BAD_REQUEST,
        )

    rows: list[LiteLLM_VerificationToken] = await _prisma_table(VerificationTokenRepository(prisma_client)).find_many(
        where={"key_alias": key_alias}, take=2
    )

    if len(rows) == 0:
        raise ProxyException(
            message=f"Key not found. No key with key_alias='{key_alias}'.",
            type=ProxyErrorTypes.not_found_error,
            param="key_alias",
            code=status.HTTP_404_NOT_FOUND,
        )

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Include "key": "<the virtual key string>" in the request body
  2. Or include "key_alias": "<unique alias>" when you don't have the raw token
  3. Check your payload serializer isn't dropping None/empty fields you meant to set
  4. Log the outgoing JSON body before sending to catch field-name mistakes

Example fix

# before
await client.post("/key/update", json={"max_budget": 100})

# after
await client.post("/key/update", json={"key": "sk-abc123", "max_budget": 100})
Defensive patterns

Strategy: validation

Validate before calling

if not payload.get("key") and not payload.get("key_alias"):
    raise ValueError("update payload must include 'key' or 'key_alias'")
await client.post("/key/update", json=payload)

Type guard

def has_key_identifier(p: dict) -> bool:
    return bool(p.get("key") or p.get("key_alias"))

Try / catch

try:
    r = await client.post("/key/update", json=payload)
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400 and "either key or key_alias" in e.response.text:
        payload["key"] = resolve_key()  # attach the identifier and resend
        r = await client.post("/key/update", json=payload)
    else:
        raise

Prevention

When it happens

Trigger: POST /key/update with only {"max_budget": 100}; a client that serializes None fields as omitted keys so both key and key_alias vanish from the JSON; passing the identifier under a wrong field name (e.g. "token" or "key_id" instead of "key"); UI form where the key selector defaulted to blank.

Common situations: Refactor renaming the request model field and forgetting the caller; SDK wrapper that builds the payload conditionally and skips identifiers when a variable is None; assuming the proxy can infer the key from auth headers used on the call.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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