fix(proxy): attribute org spend for team-linked credentials minted without org_id - #34577
Merged
ryan-crabbe-berri merged 2 commits intoJul 25, 2026
Conversation
…t org_id Keys attached to an org-linked team but minted without an organization_id produced spend that was never credited to the org: the spend writer reads user_api_key_dict.org_id with no team fallback, while the org budget check resolves the org from the team. The check therefore ran against a counter fed by almost none of the org's traffic and never tripped. Backfill org_id from the freshly fetched team object in _run_centralized_common_checks, per request only, so the spend writer and the budget check read the same org. A key with an explicitly pinned org_id always wins, and the cached key row is never mutated, so moving a team to a different org takes effect on the next auth once the team cache refreshes.
CLI session tokens from /sso/cli/poll are minted with a real team_id but no org_id, and their auth path decrypts the blob without the combined_view team join that fills org for DB keys. Spend from these tokens reached the team but never the org, so org budgets never tripped. The regression test mints a real CLI token, runs it through the centralized checks, and asserts the credential leaves auth with the team's org.
Contributor
Greptile SummaryThis PR completes request identity with the organization linked to its resolved team when the credential has no explicit organization, with regression coverage for CLI session tokens, pinned organizations, missing organizations, and failed team fetches Confidence Score: 5/5The PR appears safe to merge No blocking failure remains
|
| Filename | Overview |
|---|---|
| litellm/proxy/auth/user_api_key_auth.py | Backfills a missing request-scoped organization identity from the resolved team while preserving an explicitly assigned organization |
| tests/test_litellm/proxy/auth/test_user_api_key_auth.py | Adds focused regression coverage for organization derivation across direct, CLI-session, and team-fetch-failure scenarios |
Reviews (2): Last reviewed commit: "test(proxy): cover CLI session-token org..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
Author
yuneng-berri
approved these changes
Jul 25, 2026
ryan-crabbe-berri
merged commit Jul 25, 2026
579f41d
into
litellm_internal_staging
81 of 82 checks passed
yuneng-berri
pushed a commit
that referenced
this pull request
Jul 26, 2026
…thout org_id (#34577) * fix(proxy): attribute spend to org for team-linked keys minted without org_id Keys attached to an org-linked team but minted without an organization_id produced spend that was never credited to the org: the spend writer reads user_api_key_dict.org_id with no team fallback, while the org budget check resolves the org from the team. The check therefore ran against a counter fed by almost none of the org's traffic and never tripped. Backfill org_id from the freshly fetched team object in _run_centralized_common_checks, per request only, so the spend writer and the budget check read the same org. A key with an explicitly pinned org_id always wins, and the cached key row is never mutated, so moving a team to a different org takes effect on the next auth once the team cache refreshes. * test(proxy): cover CLI session-token org backfill from team CLI session tokens from /sso/cli/poll are minted with a real team_id but no org_id, and their auth path decrypts the blob without the combined_view team join that fills org for DB keys. Spend from these tokens reached the team but never the org, so org budgets never tripped. The regression test mints a real CLI token, runs it through the centralized checks, and asserts the credential leaves auth with the team's org. (cherry picked from commit 579f41d)
Ericcwang23
pushed a commit
to Ericcwang23/litellm
that referenced
this pull request
Jul 27, 2026
…thout org_id (BerriAI#34577) * fix(proxy): attribute spend to org for team-linked keys minted without org_id Keys attached to an org-linked team but minted without an organization_id produced spend that was never credited to the org: the spend writer reads user_api_key_dict.org_id with no team fallback, while the org budget check resolves the org from the team. The check therefore ran against a counter fed by almost none of the org's traffic and never tripped. Backfill org_id from the freshly fetched team object in _run_centralized_common_checks, per request only, so the spend writer and the budget check read the same org. A key with an explicitly pinned org_id always wins, and the cached key row is never mutated, so moving a team to a different org takes effect on the next auth once the team cache refreshes. * test(proxy): cover CLI session-token org backfill from team CLI session tokens from /sso/cli/poll are minted with a real team_id but no org_id, and their auth path decrypts the blob without the combined_view team join that fills org for DB keys. Spend from these tokens reached the team but never the org, so org budgets never tripped. The regression test mints a real CLI token, runs it through the centralized checks, and asserts the credential leaves auth with the team's org.
yuneng-berri
added a commit
that referenced
this pull request
Jul 28, 2026
ap-anton-r-susilo
pushed a commit
to ap-anton-r-susilo/litellm
that referenced
this pull request
Jul 29, 2026
…thout org_id (BerriAI#34577) * fix(proxy): attribute spend to org for team-linked keys minted without org_id Keys attached to an org-linked team but minted without an organization_id produced spend that was never credited to the org: the spend writer reads user_api_key_dict.org_id with no team fallback, while the org budget check resolves the org from the team. The check therefore ran against a counter fed by almost none of the org's traffic and never tripped. Backfill org_id from the freshly fetched team object in _run_centralized_common_checks, per request only, so the spend writer and the budget check read the same org. A key with an explicitly pinned org_id always wins, and the cached key row is never mutated, so moving a team to a different org takes effect on the next auth once the team cache refreshes. * test(proxy): cover CLI session-token org backfill from team CLI session tokens from /sso/cli/poll are minted with a real team_id but no org_id, and their auth path decrypts the blob without the combined_view team join that fills org for DB keys. Spend from these tokens reached the team but never the org, so org budgets never tripped. The regression test mints a real CLI token, runs it through the centralized checks, and asserts the credential leaves auth with the team's org. (cherry picked from commit 579f41d)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
Problem this solves:
How it solves it:
Relevant issues
Linear ticket
Resolves LIT-4688
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
A customer ran an org with a hard budget cap and watched it get exceeded: 96% of their spend was logged with an empty organization_id even though every credential carried a real, org-linked team. Their traffic authenticates with CLI session tokens from
/sso/cli/poll, whichget_cli_jwt_auth_tokenmints withteam_idset andorg_idnever set; at request time the token blob is decrypted and used as-is, so the combined_view SQL join that fills org from team for DB virtual keys never runs. Team spend and team budgets worked the whole time, which made the gap invisibleProof against a live proxy on localhost:4141 (groq, real spend), same CLI session token, same org (max_budget $0.00001) and org-linked team both runs. Seed steps:
POST /organization/newwith the cap,POST /team/newwith that organization_id,POST /user/newwith that team, then mint the session token the same way/sso/cli/polldoesBefore, at 7257d0f (base):
After, at 22607bf (this PR):
Restart durability, which is what the customer actually observed in production: on the base commit the org budget check reads an in-memory counter fed by nothing durable, so a single pod can throw one transient 429 at the threshold, and a restart (or any other pod) reseeds from the DB's 0.0 and serves traffic again. Reproduced both ways
The fix makes the DB org spend real, so enforcement survives restarts and is consistent across pods
Type
🐛 Bug Fix
Changes
_run_centralized_common_checksnow setsuser_api_key_auth_obj.org_idfromteam_object.organization_idwhen the credential has no org of its own. The mutation is per-request only, on the UserAPIKeyAuth built for this request, never the cached key row, so a team moving to a different org is honored on the next auth once the team cache refreshes. A credential with an explicitly pinned org_id always winsThis is the same derivation combined_view already does in SQL for DB virtual keys, applied at the one seam every auth path flows through, so CLI session tokens, JWT team tokens, and any future credential type get the same complete identity that keys get. The org budget check's local team fallback keeps working as before; the difference is the spend writer now sees the same org the check sees
Tests: a parametrized regression test for the backfill (backfills from team, pinned key org wins, no org anywhere stays None), a team-fetch-failure test asserting the backfill fails safe, and a CLI session-token test that mints a real token via
get_cli_jwt_auth_token, decrypts it like the auth path does, and asserts it leaves the centralized checks with the team's org. The backfill test and the session-token test both fail without the fixFinal Attestation