Failed to fetch dynamically imported module
The browser asked for a piece of your app that no longer exists, usually because a new deploy replaced it while the page was open.
Also appears as
- TypeError: error loading dynamically imported module: https://my-app.com/assets/Settings-B7x2kQ9d.js
- TypeError: Importing a module script failed.
- Error: Unable to preload CSS for /assets/Settings-Cx81aP0e.css
Prompt to fix it
Users sometimes see "Failed to fetch dynamically imported module" right after we deploy. Find where the app lazy loads pages or components with import() or React.lazy. Add a vite:preloadError listener in the app’s entry file that reloads the page once, with a guard so it can’t loop, and check that our host doesn’t cache index.html for long while hashed files in /assets stay cacheable. If it also happens in development, check the lazily loaded file for compile errors. Don’t remove code splitting to make it go away.
Paste into Layerwise or any AI coding tool.
What it means
Vite splits a built app into chunks: the main bundle plus separate files for pages and features loaded later with import(), such as React.lazy components and lazy routes. Each file name includes a hash of its contents, like Settings-B7x2kQ9d.js, so every deploy that changes the code produces new names.
A tab opened before the deploy still runs the old main bundle, and it asks for the old file names. Most hosts serve only the newest deploy, so that request gets a 404, or your index.html when a single-page app fallback answers. That’s why real visitors see this right after you ship and you almost never see it on your machine.
In the dev server, the same message usually means the lazily loaded file failed to compile, or Vite restarted while the page stayed open. The terminal shows the compile error, and a reload fixes the rest.
Common causes
- A new deploy replaced the hashed chunk files while visitors still had the previous version open.
- A browser or CDN cached
index.html, so it keeps pointing at chunks from an older deploy. - In development, the lazily loaded file has a syntax or import error, so the dev server answers with an error in place of the module.
- A browser extension or a dropped connection blocked the request for that chunk.
How to fix it
- Open the Network tab and find the failed request. A 404 or an HTML response means the file is gone; a 500 from the dev server means the file has an error.
- Listen for Vite’s
vite:preloadErrorevent in your app’s entry file and reload the page once, so visitors pick up the new deploy. - Check that
index.htmlisn’t cached for long: it should be served withCache-Control: no-cacheor a shortmax-age, while hashed files inassets/can be cached for a year. - In development, read the terminal for the compile error in that file, fix it, and reload.
Example fix
// src/main.tsx: a chunk from an older deploy is gone, so load the new one.
window.addEventListener('vite:preloadError', (event) => {
const lastReload = Number(sessionStorage.getItem('chunk-reload') ?? 0);
// Reload at most once a minute, so a real outage can't loop.
if (Date.now() - lastReload < 60_000) return;
event.preventDefault();
sessionStorage.setItem('chunk-reload', String(Date.now()));
window.location.reload();
});Related errors
All errorsFailed to load module script
The browser asked for your app’s JavaScript and got an HTML page back, so it refused to run it and the page stayed blank.
404 on page refresh
Your single-page app works from the home page, but the host has no file for deeper URLs, so reloads and shared links hit a 404.
Failed to fetch
The browser never got a usable response: the server was unreachable, or the browser blocked the request or its reply.
ERR_TOO_MANY_REDIRECTS
The site keeps redirecting the browser in a circle, usually between HTTP and HTTPS or between two domains, so the browser stops trying.
Build with an agent that checks its work
The Layerwise agent clicks through the live preview and reads the console, so it catches errors before you do.