lazyRouteComponent reload guard key collides on Safari, capping stale-deploy recovery at one per tab
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
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
- Build and deploy an app with at least two code-split routes,
/aand/b, whose chunks are content-hashed. - Open the app in Safari and stay on the home route without refreshing.
- Deploy a change that rehashes the chunks for both
/aand/b. The old chunks are no longer served. - Navigate to
/a. The import fails,isModuleNotFoundErrormatches, the guard key is set, and the tab reloads — recovered, as designed. - With that tab still open, deploy a second change that rehashes the chunks again.
- 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
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di TanStack/router
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
-
information needed
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
information needed
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
Tutte le issue di TanStack/router
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
mksglu/context-mode#1200 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
jaegertracing/jaeger-ui#4506 ·
-
area:desktop area:ui bug platform:macos
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
anthropics/claude-code#96687 ·
-
good first issue
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 95/100
AOSSIE-Org/DebateAI#582 · 2 commenti ·