Repository navigation
Conversation
A successful fetch removed usage.lock unconditionally. When two renders fetch concurrently and one receives a 429, the other's later success deleted the Retry-After lock, so the next render after the 180s cache expiry fetched again inside the server's backoff window. Clear the lock only while it still holds this render's own in-flight record. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This was referenced Sep 30, 2026
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.
BLUF
clearUsageLock(), which deletes the 429rate-limitedlock. The next render after the 180 s cache expiry then fetches again inside the server's Retry-After window.usage.lockonly while it still holds the in-flight record this render wrote (content compare).rate-limitedlock while the request is in flight. It fails onmainand passes with the fix.Details
The lock check (
readActiveUsageLock) and the in-flight write (writeUsageLock(now + 30, 'timeout')) are two separate syscalls, so two renders can both pass the check. The failing sequence:{ blockedUntil: now + retryAfter, error: 'rate-limited' }.clearUsageLock(), an unconditionalrmSync, which deletes B's lock.CACHE_MAX_AGE(180 s, shorter than the 300 s default backoff), the next render finds a stale cache and no lock, and fetches again.I found this with a TLA+ model of the lock protocol:
A replay of the same interleaving against the built
dist/bundle (network faked, 300 ms pause injected after A's lock check) shows:The window is narrow: two renders must pass the lock check at almost the same moment, and only sessions that need API-only fields fetch at all. So this is low priority. The fix is small and keeps #487's intent: a render still removes its own in-flight lock after a satisfying success.
Checks
bun run lint: clean.bun run build: clean.bun test: the new test passes.fetchUsageData error handling > preserves root errors…times out at 5 s on this loaded machine, identically onmain.🤖 Generated with Claude Code