🐛 fix(usage): clear in-flight lock on a successful fetch#487
Open
gaborbernat wants to merge 1 commit into
Open
🐛 fix(usage): clear in-flight lock on a successful fetch#487gaborbernat wants to merge 1 commit into
gaborbernat wants to merge 1 commit into
Conversation
The pre-fetch usage.lock is written as a 'timeout' guard before the API
call but is never removed on success, so it lingers for LOCK_MAX_AGE. A
cache miss in that window (e.g. an account switch invalidating the token
fingerprint) then returns getStaleUsageOrError('timeout', ...), showing a
spurious [Timeout] while the API is healthy.
Clear the lock after a successful cache write. Genuine error and
rate-limit backoff locks are untouched.
Fixes sirmalloc#486
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.
Switching Claude accounts could leave the usage widgets stuck on
[Timeout]for up to 30 seconds even though the API was healthy and the new account would have fetched fine. 🔐 The trigger is the in-flight guardfetchUsageDatawrites before every API call: ausage.lockcarrying{ error: 'timeout', blockedUntil: now + LOCK_MAX_AGE }. On a successful fetch the cache is written and returned, but that pre-fetch lock is never removed, so it lingers for the fullLOCK_MAX_AGE.Inside that window any render that misses the file cache falls through to
readActiveUsageLockand returnsgetStaleUsageOrError('timeout', …). An account switch is the clearest case: the fresh cache is rejected because the new token failstokenHashMatches, the old account's cache is also rejected by fingerprint, and the leftover lock turns the result into{ error: 'timeout' }. The label is misleading too, sinceusage.lockreadstimeoutafter a fetch that actually succeeded.The fix clears the lock once the successful response has been cached. ✨ Genuine error and rate-limit backoff locks are written on their own paths and stay untouched, so the existing "don't serve a mismatched account cache during an active lock" backoff behaviour is preserved.
Fixes #486