Skip to content

fix(proxy): always merge caller-supplied tags into request metadata - #27789

Merged
yuneng-berri merged 2 commits into
litellm_1.84.0rc2from
litellm_/wizardly-sammet-94baac
May 12, 2026
Merged

fix(proxy): always merge caller-supplied tags into request metadata#27789
yuneng-berri merged 2 commits into
litellm_1.84.0rc2from
litellm_/wizardly-sammet-94baac

Conversation

@yuneng-berri

Copy link
Copy Markdown
Collaborator

Backport of #27784 onto litellm_1.84.0rc2.

Summary

Caller-supplied tags (x-litellm-tags header, body tags, metadata.tags) were silently dropped from request metadata unless the calling key/team had metadata.allow_client_tags: true set. This broke two documented features:

  • Tag-based routing via x-litellm-tags — the documented per-request header for routing to tagged deployments.
  • Tag-based spend attributionrequest_tags going into spend logs and /spend/tags aggregations.

This PR restores the previous behavior: tags from the request always flow into metadata.tags and union with any admin-configured static tags from key/team/project metadata.

What changed

  • litellm/proxy/litellm_pre_call_utils.py — drop the conditional strip of caller tags from body / metadata / litellm_metadata and the conditional gate on the x-litellm-tags header merge. Both now run unconditionally.
  • The allow_client_tags flag is removed from the pre-call pipeline. It was only ever read here — no schema, types, or endpoint footprint — so existing values in key/team metadata are inert.

Test changes

  • Drop three tests that exercised the strip-when-not-opted-in path (no longer reachable).
  • Drop the "allow_client_tags": True fixture lines from the merge/union/multipart tests; their assertions about union behavior continue to hold.

Test plan

  • uv run pytest tests/test_litellm/proxy/test_litellm_pre_call_utils.py — 117 passed
  • uv run pytest tests/proxy_unit_tests/test_proxy_utils.py -k "spend_logs_metadata or duplicate_tags" — 69 passed
  • uv run pytest tests/test_litellm/router_strategy/test_router_tag_routing.py tests/test_litellm/proxy/auth/test_auth_checks.py — 112 passed

End-to-end verification was performed on the parent PR (#27784) against a running proxy.

Caller-supplied tags (`x-litellm-tags` header, body `tags`, `metadata.tags`)
were silently dropped unless the key/team had
`metadata.allow_client_tags: true` set. Restore the documented behavior:
tags from the request always flow into `metadata.tags` and union with any
admin-configured static tags from key/team/project metadata.

Removes the `allow_client_tags` opt-in flag from the pre-call pipeline.
The flag was only ever read here; it has no schema or endpoint footprint,
so leftover values in existing key metadata are inert.

Test cleanup mirrors the simplification: drop the three tests that
verified the strip-when-not-opted-in path, drop the `allow_client_tags`
fixture lines from the merge/union tests.
The tag-strip block was removed in the parent commit but two surrounding
comments still referenced "tags without opt-in" and "runs AFTER the
strip". Update them to describe the remaining user_api_key_* and
_pipeline_managed_guardrails strip that the snapshot/merge ordering
actually protects against.
"Ignored caller-supplied tags from header/root body: this "
"key/team does not have `allow_client_tags: true` in its metadata."
if tags is not None:
data[_metadata_variable_name]["tags"] = LiteLLMProxyRequestSetup._merge_tags(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: Client-controlled routing tags

Any authenticated caller can now add x-litellm-tags or root-level tags and have them merged into the metadata used by tag-based routing; because the strip above was removed, metadata.tags and litellm_metadata.tags also survive. A user who knows a restricted deployment tag can route their request to that tagged deployment and have spend recorded under arbitrary tag names, so keep caller-supplied tags behind an explicit admin opt-in or only merge key/team/project tags by default.

@veria-ai

veria-ai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

High: Client-controlled tags can select tagged deployments

This PR removes the opt-in gate around request tags and merges caller-provided values into routing metadata. A normal authenticated user can now supply a deployment tag in the request and have the router treat it as authoritative.


Status: 1 new · 1 open
Risk: 8/10

@greptile-apps

greptile-apps Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This is a backport of #27784 onto litellm_1.84.0rc2 that removes the allow_client_tags guard introduced after the RC branched, restoring the documented behavior where caller-supplied tags (x-litellm-tags header, body tags, metadata.tags) always union with admin-static key/team/project tags for routing and spend attribution.

  • litellm_pre_call_utils.py: Drops the 38-line allow_client_tags conditional block and its paired warning logs; the add_request_tag_to_metadata result is now always merged via _merge_tags regardless of key/team metadata.
  • Tests: Three tests that exercised the "strip when not opted in" path are converted to positive coverage of the unconditional-union behavior; allow_client_tags: True fixture lines removed from merge/multipart/duplicate-tags tests.
  • All three stated test suites pass (117 + 69 + 112).

Confidence Score: 4/5

Safe to merge as a regression fix; the only concern is that removing the flag also removes any admin opt-out path for deployments that were relying on the default-off behavior.

The core implementation is correct — caller tags are unioned with admin-static tags through the existing _merge_tags helper, the ordering (key → team → project → header) is preserved, and the 117+69+112 test passes confirm the intended behavior. The one concern is that allow_client_tags is deleted rather than defaulted to true, which means any admin who had been relying on the implicit tag-stripping default has no way to restore that restriction after this change lands.

litellm/proxy/litellm_pre_call_utils.py — specifically the absence of any replacement for the removed opt-out mechanism.

Important Files Changed

Filename Overview
litellm/proxy/litellm_pre_call_utils.py Removes the allow_client_tags gate entirely; caller-supplied tags from header, root body, and metadata.tags now unconditionally flow into request metadata and union with admin-static tags from key/team/project.
tests/test_litellm/proxy/test_litellm_pre_call_utils.py Three "strip-without-permission" tests converted to positive coverage of union/dedup behavior; two "allow_client_tags opt-in" tests removed as the condition no longer exists; remaining assertions correctly cover the new unconditional-merge path.
tests/proxy_unit_tests/test_proxy_utils.py Removes allow_client_tags: True fixture lines from spend-log-metadata and duplicate-tags tests; the core assertions about tag merging/union continue to hold unchanged.

Reviews (1): Last reviewed commit: "docs(proxy): refresh stale comments refe..." | Re-trigger Greptile

", ".join(_stripped_from),
)

# Fill in the proxy_server_request body snapshot now that metadata has

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No opt-out path for tag injection after allow_client_tags removal

The allow_client_tags guard is removed entirely, so any caller can now supply x-litellm-tags, data["tags"], or metadata.tags and have them unconditionally merged into request metadata. There is no longer any admin-controlled way to prevent this. Admins who were relying on the default-off behaviour (e.g., to stop callers from misattributing spend to another team's tag budget, or from reaching specific tag-routed deployments) have no replacement mechanism after upgrading. Per the backwards-compatibility rule, a safer approach would be to flip the default to True while keeping the flag readable, so existing allow_client_tags: false deployments retain their restriction without requiring a code change.

Rule Used: What: avoid backwards-incompatible changes without... (source)

@yuneng-berri
yuneng-berri merged commit 74bb525 into litellm_1.84.0rc2 May 12, 2026
107 of 113 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_/wizardly-sammet-94baac branch May 12, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant