fix(auth): cache auth-path team object under canonical team_id key - #31418
Conversation
|
|
Greptile SummaryFixes a cache key mismatch in the auth builder where the team object was written under the raw
Confidence Score: 5/5Safe to merge — the change is a one-line targeted correction to a cache write key that was never read back, with no behavioural change to auth logic. The auth builder now writes the team object under the same No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/auth/user_api_key_auth.py | Single-line key fix: cache write now uses f"team_id:{valid_token.team_id}" instead of the bare valid_token.team_id, aligning it with the canonical key used by all read paths. |
| tests/test_litellm/proxy/auth/test_user_api_key_auth.py | Adds a regression test that drives the real auth builder through an in-memory cache and asserts the team object lands under team_id:{id} and never under the raw team_id or a None key; all external calls are mocked, no real network access. |
Reviews (1): Last reviewed commit: "fix(auth): cache auth-path team object u..." | Re-trigger Greptile
Greptile SummaryFixes a cache key mismatch in the auth path where the team object was written under the raw
Confidence Score: 5/5Safe to merge; the change is a single targeted write-key correction with no risk of regressions on other paths. The fix is a one-character-plus-prefix change at a single call site, confirmed correct by cross-checking No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/auth/user_api_key_auth.py | Single-line fix: cache key changed from raw team_id to canonical team_id:{id} format, matching the key used by get_team_object, _cache_team_object, and _update_team_cache. |
| tests/test_litellm/proxy/auth/test_user_api_key_auth.py | New regression test drives the real auth builder with a team-scoped key, asserts the team object is stored under team_id:{id} and absent under the raw or None key; uses only mocks, no real network calls. |
Reviews (2): Last reviewed commit: "fix(auth): cache auth-path team object u..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The auth builder cached the team object under the raw `valid_token.team_id`,
while `get_team_object`, `_cache_team_object`, and `_update_team_cache` all read
and write under `team_id:{id}`. The raw-key write was therefore never served
back, and on a non-team (personal) key, whose team_id is None, the original
unguarded version passed a None key straight to the cache layer; the in-memory
cache tolerates None keys but Redis rejects them with a NoneType key error, so
with `enable_redis_auth_cache: true` the team object never reached the L2 cache
and every request fell back to Postgres.
Write under the canonical `team_id:{id}` key, keeping the existing guard that
skips the write when team_id is None. Add a regression test that drives the real
auth builder for a team-scoped key against an in-memory cache and asserts the
team object is served back under `team_id:{id}` and never under the raw team_id
or a None key.
Resolves LIT-4000
859f234 to
398961f
Compare
Relevant issues
Linear ticket
Resolves LIT-4000
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewScreenshots / Proof of Fix
On the auth path, the team object was cached under the raw
valid_token.team_id, whileget_team_object,_cache_team_object, and_update_team_cacheall read and write underteam_id:{id}. That raw-key write was never served back, and on a non-team (personal) key whoseteam_idis None the original unguarded write passed a None key straight to the cache layer; the in-memory cache tolerates None keys but Redis rejects them with a NoneType key error, so withenable_redis_auth_cache: truethe team object never reached the Redis L2 and every request fell back to Postgres.Reproduced against a live proxy with
enable_redis_auth_cache: truebacked by real Postgres and Redis, calling a real Anthropic model with a team-scoped key.Setup (same for before and after):
Before the fix, the auth path writes the full team object under the bare
lit4000-teamkey thatget_team_objectnever reads, in addition to the canonical key:After the fix, the bare orphan key is gone and the team object is cached only under the canonical
team_id:lit4000-teamthat the read path serves; the real LLM call still returns "Hi":To confirm the ticket's expected behavior end to end (the team object caches on the first lookup and subsequent requests are served from cache rather than hitting Postgres), I ran three successive requests with the same team-scoped key on a cold start (in-memory empty +
redis-cli FLUSHALL) and traced auth with OpenTelemetry v2 (LITELLM_OTEL_V2=true, console exporter). Theauthspan carries apostgreschild span per DB lookup, so a cache hit shows up as that span disappearing.Note for anyone reproducing this:
import litellmrunsload_dotenv(), which walks up to the repo.env; if that file setsOTEL_ENDPOINT, the v2 config sends spans there instead of the console. Pre-setOTEL_ENDPOINT="" OTEL_EXPORTER_OTLP_ENDPOINT="" OTEL_EXPORTER=consolebefore launch soload_dotenv(override=False)can't repopulate them.Only the cold request hits Postgres for the key and team objects; every subsequent request resolves auth entirely from the L2 cache, with zero
postgresspans underauth. The remainingredis async_set_cachespans on requests 2 and 3 are cache-refresh writes (last-active / spend), not auth reads. The same result holds in the Postgreslog_statement=allquery log (3 authSELECTs on request 1, 0 after)Type
🐛 Bug Fix
Changes
The write in
_user_api_key_auth_buildernow usesf"team_id:{valid_token.team_id}", matchingget_team_object/_cache_team_object/_update_team_cache, and keeps the existing guard that skips the write whenteam_idis None so a None key can never reach Redis.Added a regression test that drives the real auth builder for a team-scoped key against an in-memory
UserApiKeyCacheand asserts the team object is served back underteam_id:{id}and never under the rawteam_idor a None key. Reverting the key to the rawteam_id(or None) makes the canonical read miss and fails the test.