BerriAI/litellm · warning · HTTPException
Malformed request. No keys passed in.
Error message
Malformed request. No keys passed in.
What it means
POST /v2/key/info requires a KeyRequest body containing keys (and/or key_aliases) to look up. When the parsed body is None — request posted with no JSON body at all, or a body FastAPI could not bind — the handler explicitly returns 422 'Malformed request. No keys passed in.' rather than querying with an unbounded (token=None) filter.
Source
Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:3554
Returns:
Dict containing the key and its associated information
Example Curl:
```
curl -X GET "http://0.0.0.0:4000/key/info" \
-H "Authorization: Bearer sk-1234" \
-d {"keys": ["sk-1", "sk-2", "sk-3"]}
```
"""
from litellm.proxy.proxy_server import prisma_client, user_api_key_cache
try:
if prisma_client is None:
raise Exception(
"Database not connected. Connect a database to your proxy - https://docs.litellm.ai/docs/simple_proxy#managing-auth---virtual-keys"
)
if data is None:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail={"message": "Malformed request. No keys passed in."},
)
# Resolve key_aliases to tokens so we never pass token=None (unbounded query)
tokens_to_query: Final = list(data.keys) if data.keys else []
if data.key_aliases:
alias_rows: Final = await _prisma_table(VerificationTokenRepository(prisma_client)).find_many(
where={"key_alias": {"in": data.key_aliases}},
include={"litellm_budget_table": True},
)
alias_tokens: Final = [row.token for row in alias_rows if row.token]
tokens_to_query.extend(alias_tokens)
if not tokens_to_query:
return {"key": data.keys, "info": []}
key_info: Final = await prisma_client.get_data(token=tokens_to_query, table_name="key", query_type="find_all")View on GitHub (pinned to 77b7c6c40c)
Solutions
- Send a JSON body: {"keys": ["sk-1", "sk-2"]} (or key_aliases).
- Ensure Content-Type: application/json is set by your client.
- Short-circuit your calling code when the key list is empty instead of issuing the request.
Example fix
# before
client.post("/v2/key/info")
# after
client.post("/v2/key/info", json={"keys": ["sk-1", "sk-2"]}) Defensive patterns
Strategy: validation
Validate before calling
def build_key_info_body(keys: list[str] | None, key_aliases: list[str] | None) -> dict:
if not keys and not key_aliases:
raise ValueError("/v2/key/info needs non-empty 'keys' or 'key_aliases'")
body: dict = {}
if keys:
body["keys"] = keys
if key_aliases:
body["key_aliases"] = key_aliases
return body Type guard
def is_valid_key_info_request(keys: list[str] | None, key_aliases: list[str] | None) -> bool:
return bool(keys) or bool(key_aliases) Prevention
- Always send a JSON body with Content-Type: application/json on /v2/key/info.
- Short-circuit empty selections client-side before making the request.
- Assert a parseable body survives any intermediary proxy/load balancer in staging tests.
When it happens
Trigger: POST /v2/key/info with no body, an empty JSON object, or a Content-Type that doesn't parse as JSON; client code conditionally omitting the keys field and serializing {}.
Common situations: A 'get info for selected keys' feature where none are selected; forgetting -d/--data in curl; sending the keys as query params instead of the JSON body; upstream proxy/load balancer stripping the request body.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- key_ids must be provided when all_keys_in_team is False
- Invalid request type
- Maximum {MAX_BATCH_SIZE} keys can be updated at once. Found
- For SAP Masking Module Config you must provide 'providers'.
- For using SAP Filtering Module you must provide at least one
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/3ac3676ee89b8531.
Report an issue: GitHub.