Files
accounted/components
Jakob Wennberg 715c671b67 fix(app): stop transient login errors flashing the full-screen fallback (#1002)
* fix(app): stop transient login errors flashing the full-screen fallback

The BankID login landing (/auth/callback then the /select-company picker)
fires several Supabase auth/DB queries right as the session cookies are set,
so a transient failure there (most often a refresh-token rotation race, seen
in prod as "Invalid Refresh Token: Already Used/Not Found" on /middleware, or
a stale JS chunk after a deploy) threw during render. Only (dashboard) had an
error.tsx, so these escaped every boundary and hit app/global-error.tsx,
blanking the whole document with a bare "Nagot gick fel" screen for ~1s before
the next request repainted and logged the user in as usual.

Add an app-level error.tsx (AppErrorBoundary) that catches those segments and
their layouts, and harden global-error.tsx. Both recover via a single guarded
hard reload instead of React reset(): a reload re-runs middleware (fresh
rotated auth cookie) and fetches a fresh bundle (ChunkLoadError after a
deploy), matching the browser-navigation self-heal these transients already
relied on, whereas reset() re-renders against the same stale payload/bundle. A
per-path sessionStorage time-guard bounds it to one reload so a persistent
error shows the manual fallback instead of looping.

Reported via support: transient "Nagot gick fel" flash on BankID login.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(app): make the error-boundary reload guard loop-proof (review)

CodeRabbit and the PR Agent both flagged that the 12s time-window guard could
still loop if a failing render takes longer than the window (e.g. a slow SSR
that eventually throws). Replace the time window with a per-path, per-tab-session
one-shot flag, so the auto-reload fires at most once per path regardless of
timing and a genuinely persistent error settles on the manual fallback.
sessionStorage is per-tab, so a fresh visit (or a different path) still gets a
fresh auto-recovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(app): claim the reload flag atomically + add support escape hatch (review)

CodeRabbit round 2:
- Major: the one-shot flag was written in the effect but the reload fired even
  if the write threw (sessionStorage quota full), so it could reload forever
  without ever recording the attempt. Claim the flag inside decideInitialPhase
  instead, so entering the 'reloading' phase guarantees the flag persisted;
  any write failure falls through to the manual 'fallback' (no reload).
- Minor: give global-error.tsx a support escape hatch. It can't use SupportLink
  (no providers when the root layout fails), so use a dependency-free mailto to
  the hardcoded support address.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 20:05:43 +02:00
..
2026-07-05 03:05:09 +02:00
2026-07-12 19:14:12 +02:00
2026-07-12 19:14:12 +02:00
2026-07-12 19:14:12 +02:00
2026-07-12 19:14:12 +02:00
2026-07-12 19:14:12 +02:00
2026-07-12 19:14:12 +02:00