lazyRouteComponent reload guard key collides on Safari, capping stale-deploy recovery at one per tab
メンテナーはふだん 1 日以内に返信
@mithatakbulut がすでに取り組んでいます。
2026年9月16日 から。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 76/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- react, typescript
調査の方向性
sessionStorage ガードが組み立てられている packages/react-router/src/lazyRouteComponent.tsx から始め、lazyRouteComponent の呼び出し箇所を追って、利用可能な route id を確認します。同じ route に対する一度限りのリロード保護を維持しつつ、Safari で別々に発生した lazy route の失敗からはそれぞれ独立して復旧できるようにします。2 つの lazy route と、古いデプロイに起因する連続した失敗で動作を検証します。
索引モデルが issue の本文から書いたものです。
説明
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/[email protected],@tanstack/[email protected]
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.
- 主要言語
- TypeScript
- スター
- 15.2k
- フォーク
- 1.9k
- 平均マージ
- 1日 12時間
- マージ済み PR(30日)
- 106
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
TanStack/router のほかの issue
-
Inline scripts are duplicated after hydration when the CSP nonce is sent in a header対応中かも @gabriel-le が 13 日前に担当しました。 オープン
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
メンテナーはふだん 1 日以内に返信
-
start-plugin-core: sitemap lastmod and pages.json lastBuilt are stamped with the build's wall clock (no SOURCE_DATE_EPOCH support)対応中かも @treadnot が 14 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
メンテナーはふだん 1 日以内に返信
-
Empty .ts route file is scaffolded with JSX, breaking all route generation対応中かも @nathan-logan が 18 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
information needed
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
TanStack/router#8464 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
start-compiler-plugin fails to lazy-load dependencies with Vite bundledDev対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
TanStack/router#8407 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
TanStack/router の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
Agenta-AI/agenta#7542 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
code quality security
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
enhancement good first issue
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 82/100
homarr-labs/dashboard-icons#2939 ·
メンテナーはふだん 1 日以内に返信