Problem
When OAuth token acquisition fails (e.g., CSRF rejection, network error, session expiration), LocalStorageStore enters an error state with a 30-second retry wait (ERROR_WAIT = 1000 * 30). During this time, all API calls that require authentication are blocked — the frontend appears frozen with no user-visible feedback.
How It Happens
- Frontend needs an OAuth token → calls
doOauthCode()
doOauthCode() sends POST /oauth/authorize → POST /oauth/token
- If either request fails (network error, 4xx, 5xx),
LocalStorageStore.start() catches the error
- Writes
{ error: true, wait: now + 30_000 } to localStorage
- Schedules retry after 30s + jitter (0-5s)
- All calls to
LocalStorageStore.get() return { value: null, promise } — callers wait
_doTokenLogic() awaits the promise, blocking the API call
- After ~30s, retry fires — if it succeeds, everything unblocks at once
Impact
- User experience: Page appears stuck/frozen for 30+ seconds after any token failure
- No feedback: No error message, spinner, or indication of what's happening
- Cross-tab impact: Error state in localStorage affects all open tabs
- Silent failure: The 30s delay is invisible — looks like a slow server
Proposed Fixes
- Show loading/retry state in UI — Surface the
LocalStorageStore error state to the user with a "Reconnecting..." message and countdown
- Reduce ERROR_WAIT for token errors — 30 seconds is appropriate for server-side rate limiting but excessive for token failures. Consider shorter retry (5-10s) for auth-specific errors
- Add immediate retry option — Let the user click "Retry now" instead of waiting the full 30s
- Distinguish error types — Network errors vs auth failures vs rate limiting should have different retry strategies
Files
packages/frontend-data/src/lib/local-storage-store.ts — ERROR_WAIT, scheduleRetry()
packages/frontend-data/src/lib/api-ganymede.ts — _doTokenLogic() wait loop
packages/frontend-data/src/lib/oauth-client.ts — doOauthCode() request chain
Problem
When OAuth token acquisition fails (e.g., CSRF rejection, network error, session expiration),
LocalStorageStoreenters an error state with a 30-second retry wait (ERROR_WAIT = 1000 * 30). During this time, all API calls that require authentication are blocked — the frontend appears frozen with no user-visible feedback.How It Happens
doOauthCode()doOauthCode()sendsPOST /oauth/authorize→POST /oauth/tokenLocalStorageStore.start()catches the error{ error: true, wait: now + 30_000 }to localStorageLocalStorageStore.get()return{ value: null, promise }— callers wait_doTokenLogic()awaits the promise, blocking the API callImpact
Proposed Fixes
LocalStorageStoreerror state to the user with a "Reconnecting..." message and countdownFiles
packages/frontend-data/src/lib/local-storage-store.ts—ERROR_WAIT,scheduleRetry()packages/frontend-data/src/lib/api-ganymede.ts—_doTokenLogic()wait looppackages/frontend-data/src/lib/oauth-client.ts—doOauthCode()request chain