BerriAI/litellm · error · HTTPException
Invalid sort order. Must be 'asc' or 'desc'
Error message
Invalid sort order. Must be 'asc' or 'desc'
What it means
Raised by the LiteLLM proxy key-management endpoints when a list request supplies a sort_order value that is not 'asc' or 'desc'. The helper that builds the Prisma order_by clause validates the column first (sort_by) and then lower-cases sort_order and checks membership in ['asc','desc']; anything else gets an HTTP 400 before any query runs. It exists to prevent arbitrary strings from reaching the database layer.
Source
Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:5795
return None
# Validate sort_by is a valid column
valid_columns: Final = [
"spend",
"max_budget",
"created_at",
"updated_at",
"token",
"key_alias",
]
if sort_by not in valid_columns:
raise HTTPException(
status_code=400,
detail={"error": f"Invalid sort column. Must be one of: {', '.join(valid_columns)}"},
)
# Validate sort_order
if sort_order.lower() not in ["asc", "desc"]:
raise HTTPException(
status_code=400,
detail={"error": "Invalid sort order. Must be 'asc' or 'desc'"},
)
order_by[sort_by] = sort_order.lower()
return order_by
def _build_expires_where_clause(expires_filter: str, now: datetime) -> dict[str, object]:
if expires_filter == "expired":
return {"AND": [{"expires": {"not": None}}, {"expires": {"lt": now}}]}
return {"OR": [{"expires": None}, {"expires": {"gte": now}}]}
def _build_key_filter_conditions(
user_id: str | None,
team_id: str | None,View on GitHub (pinned to 77b7c6c40c)
Solutions
- Send exactly 'asc' or 'desc' (any letter casing) for sort_order, e.g. /key/list?sort_by=token&sort_order=desc
- Fix the UI/SDK mapping so 'ascending'->'asc' and 'descending'->'desc' before the request leaves the client
- URL-encode the query string properly and confirm no stray whitespace or newlines are appended
- If the 400 mentions the column instead, also verify sort_by is one of the valid columns listed in the error detail (includes token, key_alias, updated_at, etc.)
Example fix
# before curl 'http://localhost:4000/key/list?sort_by=token&sort_order=ascending' # after curl 'http://localhost:4000/key/list?sort_by=token&sort_order=desc' # 'ASC'/'Desc' also OK; server lower-cases
Defensive patterns
Strategy: validation
Validate before calling
SORT_ORDERS = ('asc', 'desc')
def normalize_list_params(params: dict) -> dict:
if 'sort_order' in params:
so = str(params['sort_order']).strip().lower()
if so not in SORT_ORDERS:
raise ValueError(f"sort_order must be one of {SORT_ORDERS}, got {params['sort_order']!r}")
params['sort_order'] = so
return params Type guard
def is_valid_sort_order(value: object) -> bool:
return isinstance(value, str) and value.strip().lower() in ('asc', 'desc') Try / catch
try:
r = await client.get('/key/list', params=normalize_list_params(q))
r.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 400 and 'sort order' in e.response.text.lower():
q['sort_order'] = 'desc' # correct and retry once
r = await client.get('/key/list', params=q)
else:
raise Prevention
- Map UI labels 'ascending'/'descending' to 'asc'/'desc' at the client boundary
- Keep sort_order values in a shared constant/enum instead of free-form strings
- Strip and lower-case the value before putting it in the URL
When it happens
Trigger: Calling GET /key/list (or any key-listing route that accepts sort_by/sort_order) with e.g. sort_order=ascending, sort_order=ASCENDING, sort_order='' (empty), sort_order='DESC ' (trailing whitespace), or a URL-mangled value like sort_order=desc%0A. Case-insensitive: 'ASC'/'Desc' pass because the check applies .lower() first.
Common situations: Frontend dropdowns that send 'ascending'/'descending' instead of 'asc'/'desc'; copy-pasted query strings from other APIs (e.g. Grafana-style 'ASC' is fine but '+desc' from unencoded spaces is not); typos like 'ascc' or 'dsc'; SDKs defaulting to 'descending' when no explicit value is set.
Related errors
- Please provide start_date and end_date
- Invalid key format.
- Invalid model_max_budget: {e}. Example of valid model_max_bu
- urn:litellm:error:unknown-query-parameter
- urn:litellm:error:unknown-query-parameter
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/2f7f07fa47f8bdb7.
Report an issue: GitHub.