BerriAI/litellm · error · ProxyException
Client-side 'metadata.tags' not allowed in request. 'reject_
Error message
Client-side 'metadata.tags' not allowed in request. 'reject_clientside_metadata_tags'={general_settings['reject_clientside_metadata_tags']}. Tags can only be set via API key metadata. What it means
Raised by _reject_clientside_metadata_tags_check when general_settings.reject_clientside_metadata_tags is true and a POST to an LLM route carries metadata.tags in the body. With this flag, tags become server-managed: they may only be attached to virtual keys via key metadata, never injected by the caller, so clients cannot spoof cost grouping.
Source
Thrown at litellm/proxy/auth/auth_checks.py:537
is_mcp_route: Final = route in LiteLLMRoutes.mcp_routes.value or RouteChecks.check_route_access(
route=route, allowed_routes=LiteLLMRoutes.mcp_routes.value
)
if is_post_method and is_openai_route and not is_mcp_route and "user" not in request_body:
raise Exception(f"'user' param not passed in. 'enforce_user_param'={general_settings['enforce_user_param']}")
def _reject_clientside_metadata_tags_check(general_settings: dict, request_body: dict, route: str) -> None:
if not general_settings.get("reject_clientside_metadata_tags", False):
return
if (
RouteChecks.is_llm_api_route(route=route)
and "metadata" in request_body
and isinstance(request_body["metadata"], dict)
and "tags" in request_body["metadata"]
):
raise ProxyException(
message=f"Client-side 'metadata.tags' not allowed in request. 'reject_clientside_metadata_tags'={general_settings['reject_clientside_metadata_tags']}. Tags can only be set via API key metadata.",
type=ProxyErrorTypes.bad_request_error,
param="metadata.tags",
code=status.HTTP_400_BAD_REQUEST,
)
def _global_proxy_budget_check(global_proxy_spend: float | None, skip_budget_checks: bool, route: str) -> None:
if (
litellm.max_budget > 0
and not skip_budget_checks
and global_proxy_spend is not None
and RouteChecks.is_llm_api_route(route=route)
and route != "/v1/models"
and route != "/models"
):
if math.isfinite(litellm.max_budget) and global_proxy_spend > litellm.max_budget:
raise litellm.BudgetExceededError(View on GitHub (pinned to 77b7c6c40c)
Solutions
- Remove metadata.tags from the client request payload
- Set the tags on the virtual key instead: POST /key/update with metadata.tags for that key
- Keep any other metadata you need, just drop the tags key from the dict
Example fix
# before
body = {"model": "gpt-4o", "messages": [...], "metadata": {"tags": ["team-a", "prod"]]}
# after
body = {"model": "gpt-4o", "messages": [...], "metadata": {"request_id": "abc"}}
# tags live on the key: curl -X POST /key/update -d '{"key": "sk-...", "metadata": {"tags": ["team-a", "prod"]}}' Defensive patterns
Strategy: validation
Validate before calling
def strip_client_tags(body: dict) -> dict:
metadata = body.get("metadata")
if isinstance(metadata, dict):
metadata.pop("tags", None)
if not metadata:
body.pop("metadata")
return body
payload = strip_client_tags(payload) Try / catch
try:
resp = client.chat.completions.create(**payload)
except Exception as e:
if "reject_clientside_metadata_tags" in str(e):
payload = strip_client_tags(payload)
resp = client.chat.completions.create(**payload)
else:
raise Prevention
- Centralize tagging policy: tags belong on keys via key metadata, not on requests
- Audit SDK callbacks (LangChain etc.) for metadata injection when enabling the flag
- Announce the flag flip to all API consumers before enforcing it
When it happens
Trigger: Flag enabled in config, then any request containing {"metadata": {"tags": ["team-a"]}} in the body hits /v1/chat/completions or another LLM route. Only dict-valued metadata containing a tags key is rejected.
Common situations: After the operator enables the flag, LangChain callbacks or internal dashboards that tag requests client-side for cost reporting start getting 400s; shared SDK code sends metadata.tags by default.
Related errors
- messages parameter is required
- 'user' param not passed in. 'enforce_user_param'={general_se
- Only premium users can add tags to keys. {CommonProxyErrors.
- callback_name is required in key_logging
- LLM Router not initialized. Ensure models added to proxy.
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/13701e155b146bc9.
Report an issue: GitHub.