From 2af6157265c16a1852df491c9436c21dc5ce339f Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Wed, 12 Aug 2026 19:36:52 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20recover=20from=20stale=20?= =?UTF-8?q?lazy-loaded=20chunks=20after=20a=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Route components are code-split with content-hashed filenames. A user who loaded the app before a deployment (typically someone sitting in a call) still holds an `index.html` referencing old chunk names. When they navigate after the deploy — e.g. to `/feedback` on leaving a room — the old chunk is gone and the dynamic import fails with "TypeError: error loading dynamically imported module". Reload the page on that failure to fetch a fresh `index.html` with the current hashes, which transparently fixes the stale-deploy case. Guard against infinite reload loops with a `RELOAD_COOLDOWN_MS`: if the import fails again right after a reload, the cause is not a stale deploy (ad blocker, proxy, outage) and reloading further would loop forever. In that case, let the error propagate so it reaches monitoring. Inspired by https://vite.dev/guide/build#load-error-handling --- CHANGELOG.md | 4 ++++ src/frontend/src/main.tsx | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 404e8084..3caf157b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to ## [Unreleased] +### Fixed + +- ✨(frontend) recover from stale lazy-loaded chunks after a deploy + ## [1.26.0] - 2026-08-12 ### Added diff --git a/src/frontend/src/main.tsx b/src/frontend/src/main.tsx index ed319314..75661d2d 100644 --- a/src/frontend/src/main.tsx +++ b/src/frontend/src/main.tsx @@ -2,6 +2,26 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.tsx' +const CHUNK_RELOAD_KEY = 'vite-preload-error-reload-at' +const RELOAD_COOLDOWN_MS = 30_000 + +window.addEventListener('vite:preloadError', (event) => { + try { + const lastReloadAt = Number(sessionStorage.getItem(CHUNK_RELOAD_KEY)) || 0 + if (Date.now() - lastReloadAt <= RELOAD_COOLDOWN_MS) { + // Recent reload didn't help: not a stale deploy, surface the error. + return + } + sessionStorage.setItem(CHUNK_RELOAD_KEY, String(Date.now())) + } catch { + // Without sessionStorage we cannot guard against a reload loop: + // don't auto-reload, let the error propagate. + return + } + event.preventDefault() + window.location.reload() +}) + ReactDOM.createRoot(document.getElementById('root')!).render(