fix(proxy): cache invalidation double-hashes token in bulk update and key rotation#25552
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR fixes two remaining double-hashing bugs in cache invalidation paths that were missed by PR #24969. When a pre-hashed token ID (not Confidence Score: 5/5Safe to merge — minimal, correct fix with no behavioural change for the happy path and proper test coverage for both fixed call sites. The two-line production change is a precise, well-scoped fix that mirrors the pattern already applied in PR #24969. Both fixed call sites now have dedicated unit tests asserting the token is passed through unchanged. No regressions are introduced; the fix only affects callers supplying pre-hashed tokens (the previously broken path). All remaining observations are P2 or lower. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/management_endpoints/key_management_endpoints.py | Replaces hash_token() with _hash_token_if_needed() in two cache invalidation call sites (_process_single_key_update L1852 and _execute_virtual_key_regeneration L3724), correctly fixing double-hashing of pre-hashed tokens |
| tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py | Adds two async unit tests verifying both fixed call sites pass pre-hashed tokens through to _delete_cache_key_object unchanged; no real network calls; all mocked correctly |
Sequence Diagram
sequenceDiagram
participant Client
participant BulkUpdate as _process_single_key_update
participant Regen as _execute_virtual_key_regeneration
participant DB as Prisma DB
participant Cache as user_api_key_cache
Note over Client,Cache: /key/bulk_update with pre-hashed token ID
Client->>BulkUpdate: key is pre-hashed token
BulkUpdate->>DB: update_data with token
Note over BulkUpdate: _hash_token_if_needed(key) returns token as-is
BulkUpdate->>Cache: _delete_cache_key_object with correct hash
Note over Client,Cache: /key/regenerate with pre-hashed token ID
Client->>Regen: key is pre-hashed token
Regen->>DB: update verificationtoken record
Note over Regen: _hash_token_if_needed(key) returns token as-is
Regen->>Cache: _delete_cache_key_object with correct hash
Note over Client,Cache: Before fix: hash_token produced wrong hash, cache miss, stale entry persisted
Reviews (2): Last reviewed commit: "fix(proxy): use _hash_token_if_needed fo..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…update and key rotation Two code paths in key_management_endpoints.py call hash_token() unconditionally when invalidating the user_api_key_cache after a key update. When the caller passes a pre-hashed token ID (not an sk- prefixed key), hash_token() double-hashes it, producing a cache key that does not match the actual cached entry. Cache invalidation silently fails. This is compounded by update_cache() which writes the stale cached key object back with a fresh 60s TTL after every successful request, preventing natural TTL expiry. The stale entry (with outdated fields like max_budget=None) persists indefinitely under load. PR BerriAI#24969 fixed this in update_key_fn but missed two other call sites: - _process_single_key_update (bulk update path) - _execute_virtual_key_regeneration (key rotation path) Fix: replace hash_token() with _hash_token_if_needed() in both locations, matching the pattern already used elsewhere in the file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fedb4c9 to
82f2006
Compare
f54e4e6
into
BerriAI:litellm_oss_staging_04_11_2026
Summary
hash_token()calls in cache invalidation paths double-hash pre-hashed token IDs, causing cache invalidation to silently fail_hash_token_if_needed()(which passes pre-hashed tokens through unchanged), matching the pattern established by PR fix: allow hashed token_id in /key/update endpoint #24969Root cause
When
/key/updateor/key/bulk_updateis called with a pre-hashed token ID (not ansk-prefixed key),hash_token()produces a hash-of-hash that does not match the actual cache key. The_delete_cache_key_objectcall deletes nothing, and the stale cached key object (with outdated fields) remains.This is compounded by
update_cache()inproxy_server.py, which writes the stale cached key object back touser_api_key_cachewith a fresh 60s TTL after every successful LLM request. As long as requests keep flowing, the stale entry never expires — the updated fields (e.g.max_budget,models,metadata) are never picked up.PR #24969 fixed this in
update_key_fnbut missed two other call sites:_process_single_key_updatehash_token(key_update_item.key)_hash_token_if_needed(key_update_item.key)_execute_virtual_key_regenerationhash_token(key)_hash_token_if_needed(key)Reproduction steps
sk-key):/key/infothat the DB was updated (showsmax_budget: 100)max_budget: nullupdate_cache)Related
update_key_fn(merged April 3, 2026)Test plan
test_process_single_key_update_cache_invalidation_with_token_hash— verifies the token hash is passed as-is (not double-hashed) to_delete_cache_key_objecttest_process_single_key_updatecontinues to passGenerated with Claude Code