fix(tradein): RouteGuard prod 401 → session-expired screen, stop re-subscribe storm (#800) #896

Merged
bot-reviewer merged 1 commit from feat/800-routeguard-401-session-screen into main 2026-05-31 13:17:45 +00:00
Collaborator

Root cause

TanStack Query's staleTime: Infinity applies only to successful queries — errored queries are always stale. When useMe returns 401, every new observer subscription triggers a refetch (onSubscribe → executeFetch), regardless of retry: false.

RouteGuard was mounting <>{children}</> on 401, causing the entire page subtree to continuously re-subscribe → 616+ identical requests to /me and /quota in 15s (confirmed by design-loop Playwright repro, comment #7506 on #800).

This is NOT a retry issue — retry: false was already set. It's a subscribe-on-stale-error loop.

Fix

On 401 in production, RouteGuard now renders <NoAccessScreen variant="session" /> instead of mounting the app children:

  • No children mounted → no re-subscribe churn → ≤1 request on persistent 401
  • User sees: "Сессия истекла — Обновите страницу, чтобы войти снова"

In development (NODE_ENV !== "production"), the passthrough is preserved — next dev without Caddy basic_auth still works normally.

Changes

  • RouteGuard.tsx: 401 branch → dev passthrough / prod session screen
  • NoAccessScreen.tsx: add "session" variant (title + subtitle)

DoD verification

  • Стойкий 401 в prod → <NoAccessScreen variant="session" /> рендерится, subtree не монтируется, цикл не возникает.
  • Dev (NODE_ENV !== "production") → children монтируются как раньше.
  • npx tsc --noEmit — clean.
  • NoAccessScreen принимает variant="session" без TS-ошибок.

Refs #800.

## Root cause TanStack Query's `staleTime: Infinity` applies only to **successful** queries — errored queries are always stale. When `useMe` returns 401, every new observer subscription triggers a refetch (`onSubscribe → executeFetch`), regardless of `retry: false`. RouteGuard was mounting `<>{children}</>` on 401, causing the entire page subtree to continuously re-subscribe → **616+ identical requests to `/me` and `/quota` in 15s** (confirmed by design-loop Playwright repro, comment #7506 on #800). This is NOT a retry issue — `retry: false` was already set. It's a subscribe-on-stale-error loop. ## Fix On 401 in **production**, `RouteGuard` now renders `<NoAccessScreen variant="session" />` instead of mounting the app children: - No children mounted → no re-subscribe churn → ≤1 request on persistent 401 - User sees: **"Сессия истекла — Обновите страницу, чтобы войти снова"** In **development** (`NODE_ENV !== "production"`), the passthrough is preserved — `next dev` without Caddy basic_auth still works normally. ## Changes - `RouteGuard.tsx`: 401 branch → dev passthrough / prod session screen - `NoAccessScreen.tsx`: add `"session"` variant (title + subtitle) ## DoD verification - ✅ Стойкий 401 в prod → `<NoAccessScreen variant="session" />` рендерится, subtree не монтируется, цикл не возникает. - ✅ Dev (`NODE_ENV !== "production"`) → children монтируются как раньше. - ✅ `npx tsc --noEmit` — clean. - ✅ `NoAccessScreen` принимает `variant="session"` без TS-ошибок. Refs #800.
bot-frontend added 1 commit 2026-05-31 13:07:54 +00:00
TanStack Query errored queries (staleTime: Infinity) are always stale — each
new observer subscription triggers a refetch regardless of retry:false.
RouteGuard mounting children on 401 caused continuous re-subscribe churn
(616+ requests in 15s confirmed by design-loop repro 2026-05-31).

Fix: on 401 in production, render NoAccessScreen variant="session" instead
of mounting the app subtree. Dev without Caddy keeps the passthrough
(process.env.NODE_ENV !== "production") so local next dev still works.

Also adds NoAccessScreen "session" variant (title: Сессия истекла,
subtitle: Обновите страницу, чтобы войти снова).
bot-frontend added the
priority/p2
scope/frontend
status/review
tradein
labels 2026-05-31 13:08:02 +00:00
bot-reviewer merged commit 191ed03d3c into main 2026-05-31 13:17:45 +00:00
bot-reviewer deleted branch feat/800-routeguard-401-session-screen 2026-05-31 13:17:45 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#896
No description provided.