Skip to content

fix(web): expired token on load → Login instead of stuck on Loading#182

Merged
finedesignz merged 1 commit into
mainfrom
fix/expired-token-stuck-loading
May 29, 2026
Merged

fix(web): expired token on load → Login instead of stuck on Loading#182
finedesignz merged 1 commit into
mainfrom
fix/expired-token-stuck-loading

Conversation

@finedesignz
Copy link
Copy Markdown
Owner

Third facet of the auth-expiry hardening (after #180 blank-guard + #181 non-array crash). Opening the app with a dead/expired remo_token left the user stuck on the 'Loading…' screen forever.

Cause: useProfile swallows the /api/profile 401 → loading=false, profile=null; App gate profileLoading || !profile trapped on LoadingScreen. The global 401→signOut handler races useProfile's fetch on fresh load, so the redirect was dropped and the token never cleared.

Fix (App.tsx, +24/−1): split the gate (profileLoading→Loading, then !profile→), plus an effect that clears the dead credential via signOut() when load settles with no profile but a token present (outside render; no loop — verified). Happy path unaffected (useProfile never nulls profile after a successful load).

Verified (Playwright, local build, /api/profile→401): stale token on #/ and #/settings → lands on Login, token cleared to null, no loop/crash/blank. no-token load → Login. tsc 0, build 0.

🤖 Generated with Claude Code

…creen

Opening the app with an already-expired/invalid legacy localStorage token
left the user permanently on the "Loading…" screen. useProfile swallows the
/api/profile 401 and sets loading=false with profile=null; the combined
`profileLoading || !profile` render gate then trapped on LoadingScreen forever.
The global onAuthEvent('unauthorized') handler that normally clears the token
races the useProfile fetch on a fresh page-load (the handler can be null when
the 401 lands), so the redirect was dropped.

Split the render gate so a settled-but-null profile renders Login
deterministically, and add an effect that clears the dead credential
(signOut, never during render) once the load settles with no profile. signOut
nulls token+user so the effect's `(token||user)` guard goes false on the next
render → no loop. Happy path is unaffected: useProfile never nulls profile
after a successful load, so a logged-in user can't be bounced to Login on a
transient refetch blip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@finedesignz finedesignz merged commit a4494fa into main May 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant