Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

lazyRouteComponent reload guard key collides on Safari, capping stale-deploy recovery at one per tab

Aperta Adatta ai principianti
#8,331 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
76/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
react, typescript
Ambito
frontend, web-dev

Direzione di ricerca

Inizia in packages/react-router/src/lazyRouteComponent.tsx, dove viene assemblata la protezione di sessionStorage, e segui il sito di chiamata di lazyRouteComponent per confermare il route id disponibile. Mantieni la protezione dal reload una tantum per la stessa route, consentendo al contempo il recupero indipendente da errori separati di Safari delle lazy route; verifica il comportamento su due lazy route e su errori successivi dovuti a deployment obsoleti.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Which project does this relate to?

Router

Describe the bug

lazyRouteComponent's stale-deploy recovery guards its one-time reload with a sessionStorage key derived solely from the error message:

https://github.com/TanStack/router/blob/main/packages/react-router/src/lazyRouteComponent.tsx

const storageKey = `tanstack_router_reload:${error.message}`
if (!sessionStorage.getItem(storageKey)) {
  sessionStorage.setItem(storageKey, '1')
  window.location.reload()
  throw new Promise(() => {})
}

That works when the message identifies the chunk, which it does on Chrome and Firefox — both append the module URL. It does not on Safari, whose message is the bare string Importing a module script failed. with no URL. isModuleNotFoundError's own comments record this difference:

// chrome: "Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split"
// firefox: "error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split"
// safari: "Importing a module script failed."

So on Safari the key collapses to a single value — tanstack_router_reload:Importing a module script failed. — shared by every lazy route in the application, for the lifetime of the tab.

The consequence is that a Safari tab gets one recovery, ever, no matter how many separate chunks fail or how many deploys it lives through. The first stale chunk reloads and recovers; every subsequent one finds the key already set, skips the reload, and rethrows — leaving the user on the error component with no automatic recovery, which is the exact situation the guard exists to prevent.

Chrome and Firefox are unaffected: each chunk URL yields its own key, so the guard does what it is meant to do (suppress a reload loop for one chunk that stays missing, while still recovering other chunks).

Note this is distinct from #7633. That issue is about isModuleNotFoundError failing to classify certain failures; this is about the guard key being non-unique once a failure has been correctly classified. Widening the predicate does not address it, and #7653 (now closed) would not have.

Complete minimal reproducer

No hosted reproducer — this is reasoned from the source above rather than from a running app, because reproducing it needs two successive deploys against one long-lived Safari tab. The conditions are exact and the code path is short, so I hope the inspection is enough to act on; happy to build a reproducer if that is the blocker.

The observable prerequisite (Safari's message carrying no URL) is already asserted by the router's own source comments, quoted above.

Steps to Reproduce the Bug
  1. Build and deploy an app with at least two code-split routes, /a and /b, whose chunks are content-hashed.
  2. Open the app in Safari and stay on the home route without refreshing.
  3. Deploy a change that rehashes the chunks for both /a and /b. The old chunks are no longer served.
  4. Navigate to /a. The import fails, isModuleNotFoundError matches, the guard key is set, and the tab reloads — recovered, as designed.
  5. With that tab still open, deploy a second change that rehashes the chunks again.
  6. Navigate to /b (or back to /a).
Expected behavior

Step 6 performs one reload and recovers, the same as step 4 did — the guard should suppress a repeated reload for the same failing chunk, not for every chunk in the app.

Actual behavior

Step 6 performs no reload. sessionStorage already holds tanstack_router_reload:Importing a module script failed. from step 4, so the guard declines and the error is rethrown to the error component. The user is stuck until they refresh by hand.

Platform
  • Browser: Safari (any version; the behavior follows from Safari's error message wording)
  • Affected packages: @tanstack/react-router (guard), @tanstack/router-core (message classification)
  • Versions: reproduces on current latest — @tanstack/react-router@1.170.34, @tanstack/router-core@1.171.29
Additional context

A fix would be to key the guard on something that identifies the chunk rather than on the message alone — the route id available at the lazyRouteComponent call site is the obvious candidate, since it is stable across deploys and unique per split route:

const storageKey = `tanstack_router_reload:${routeId}:${error.message}`

That preserves the existing loop protection (the same route failing twice in one tab still only reloads once) while letting a different route recover on its own.

Found while tracking down why a stale tab was not recovering after a deploy on a TanStack Start app. The server-side half of that turned out to be ours to fix, but this guard caps how well the built-in recovery can work on Safari regardless.

Lingua principale
TypeScript
Stelle
15.1k
Fork
1.9k
Merge medio
1g 22h
PR unite (30g)
134

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di TanStack/router

Tutte le issue di TanStack/router

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.