vite: TypeError: Failed to fetch dynamically imported module

Describe the bug

Since switching to Vite we noticed a new production issue, where sometimes users are encountering an error if we deploy while they have an active session:

TypeError: Failed to fetch dynamically imported module

I believe this is because if any code is modified in an area that Vite would turn into a dynamic module, then the file hash changes, however when they try to visit an area that would trigger the dynamic load, those files no longer exist so they hit the error message above.

Quoting from https://stackoverflow.com/a/74057337/21061

When you dynamically import a route/component, during build it creates a separate chunk. By default, chunk filenames are hashed according to their content – Overview.abc123.js. If you don’t change the component code, the hash remains the same. If the component code changes, the hash changes too - Overview.32ab1c.js. This is great for caching.

Now this is what happens when you get this error:

  • You deploy the application
  • Your Home chunk has a link to /overview route, which would load Overview.abc123.js
  • Client visits your site
  • You make changes in your code, not necessarily to the Overview component itself, but maybe to some children components that Overview imports.
  • You deploy changes, and Overview is built with a different hash now - Overview.32ab1c.js
  • Client clicks on /overview link - gets the Failed to fetch dynamically imported module error, because Overview.abc123.js no longer exists

That is why the errors correlate with deployments. One way to fix it is to not use lazy loaded routes, but that’s not a great solution when you have many heavy routes - it will make your main bundle large

What I expect to happen, is not to encounter any errors if the users session remains active during a deployment.

I have been unable to come up with a good workaround (specifically for me using React ErrorBoundary is the best I can do so far with a re-direct similar to https://stackoverflow.com/a/74861436/21061 which is a mitigation and provides quite a poor user experience flashing an error message).

Reproduction

https://github.com/IPWright83/vite-dynamic-import

Steps to reproduce

The above repository has been set up to mimick a production deployment as obviously that is a much more complicated set-up. It leverages React.lazy to force a dynamic module and uses a setTimeout to provide a delay with which to simulate a user navigation to a page requiring a module. In a real production scenario I don’t believe React.lazy is required.

  • Clone the above repository

  • npm install

  • npm run build

  • open a 2nd terminal

  • Terminal 1 npx serve dist (starts a web server)

  • Browser open the URL (usually localhost:3000)

  • Text Editor modify src/Foo.jsx changing the string “Foo” to something else (within 30s of launching the page - increase the setTimeout in App.jsx if this is not long enough)

  • Terminal 2 npm run build

Wait… 30s after loading the page you should see a blank page render with errors in the browser console:

image

If you were to reload the page, you can see that Foo-b53985a6.js has been renamed to Foo-535d5a10.js (or similar new hash)

System Info

❯ npx envinfo --system --npmPackages '{vite,@vitejs/*}' --binaries --browsers

  System:
    OS: Linux 5.15 Ubuntu 20.04.5 LTS (Focal Fossa)
    CPU: (12) x64 Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
    Memory: 18.36 GB / 31.10 GB
    Container: Yes
    Shell: 5.0.17 - /bin/bash
  Binaries:
    Node: 16.14.0 - ~/.nvm/versions/node/v16.14.0/bin/node
    Yarn: 1.22.18 - ~/.nvm/versions/node/v16.14.0/bin/yarn
    npm: 8.3.1 - ~/.nvm/versions/node/v16.14.0/bin/npm
  Browsers:
    Chrome: 109.0.5414.74
    Chromium: 109.0.5414.74
    Firefox: 109.0


### Used Package Manager

pnpm

### Logs

_No response_

### Validations

- [X] Follow our [Code of Conduct](https://github.com/vitejs/vite/blob/main/CODE_OF_CONDUCT.md)
- [X] Read the [Contributing Guidelines](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md).
- [X] Read the [docs](https://vitejs.dev/guide).
- [X] Check that there isn't [already an issue](https://github.com/vitejs/vite/issues) that reports the same bug to avoid creating a duplicate.
- [X] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to [vuejs/core](https://github.com/vuejs/core) instead.
- [X] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/vitejs/vite/discussions) or join our [Discord Chat Server](https://chat.vitejs.dev/).
- [X] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 133
  • Comments: 151 (11 by maintainers)

Commits related to this issue

Most upvoted comments

For what it’s worth, an ad-blocker was the root of this issue for me. Disabling it resolved my issue.

@IPWright83 Meanwhile, I also saw someone handling the problem this way

router.onError((error, to) => {
  if (error.message.includes('Failed to fetch dynamically imported module')) {
    window.location = to.fullPath
  }
})

It’s surprising that this issue is still occurring with Vite 5. It’s incredible that it hasn’t been fixed yet!.

PS: is anybody from the Vite team looking at this?

Disclaimer: I am not affiliated with the vite project, but this thread is becoming less and less useful as more people pile onto the discussion without adding useful information.

Not discounting that there could be a bug here, but certainly some of the issues people have described in this thread are not issues with vite.

Dynamic imports

If your project has started using dynamic imports for the first time as a consequence of moving to vite, there are some things you should be aware of.

Deployment strategy

When using dynamic imports, your deployment process might need to be adjusted to keep previous builds/hashes available for your users. Users won’t all “update” to new versions at the same time, and the code running in their browser will request old hashes/chunks until they reload the app/page and receive a new HTML document that uses the newest entry point file. Even if you alert users that a new version is available, unless you force them to reload, you cannot get around this.

If your deployment process removes previous builds, or previous builds are not available from your hosting for any reason, you will continue to see this error.

Network failures

If your project uses dynamic imports, you are guaranteed to run into this error from time to time when your users use an ad-blocker or experience a network outage, the same as any network request. Their browser will request a dynamic chunk, it will fail on the network, and the browser will throw this error.

Unlike XHR requests, the browser will never let you retry the import, because the actual HTML spec says browsers should cache failed script requests to prevent side effects from running multiple times, despite the fact that this makes no sense for failures to fetch the module.

While some in this thread have shared libraries and strategies to mitigate this issue, none of them are 100% effective, because you only have control of the dynamic imports in your code, ie import('my-scipt.js'). While you can change this to import('my-script.js?t=123') to get around the caching issue, if the module that failed to load was a dependency of my-script.js, there is no way for you to add a query string to that import to force a retry.

Until the browser spec changes, there is literally nothing vite can do to prevent this error in the case of network failures. If you would like the browser spec to be changed, please add your support to https://github.com/whatwg/html/issues/6768.

Mitigation

At the moment, there’s not a great mitigation for these issues. Options seem to be:

  • return to webpack
  • don’t use lazy loading / dynamic imports
  • catch this error and display a message to your user asking them to reload, or force them to reload automatically

What can vite do?

Other than providing better guidance to developers on some of the above, and throwing some weight behind the change to the browser spec, I’m not sure what the vite project can do to resolve this issue.

For most of us hitting this issue in production, is vite even part of the equation? Production builds are produced by rollup, which vite is just configuring for us. I’m certainly not serving my production app with vite dev or vite preview.

If you are coming to this issue because “it only started after I switched to vite”, please make sure you understand all of the implications of using dynamic imports before adding more noise to this issue.

Why is this pending triage from January?

Because it isn’t a trivial issue, apparently.

I’m getting production issues because of this, what’s the fix, other than removing lazy loading?

You can have your money back or go to Vite’s competitors for better ROI.

Seriously though, this isn’t helpful. Vite is an open source project and you/your company can help fix the issue or sponsor it. Bitching about your problems with a tool that community members maintain for free is not helpful.

I think the majority of people here have the issue in production. I am still working on the nuxt.js-like fix to reload the page when new Vite error shows up, but considering it’s a cache issue, you have to reload the page

P.S. I do hope Vite does provide a more config-free way of handling this. The new event emit is helpful, but I wish Vite would handle this automatically, considering how many people suffer from this error

I think there are possibly two separate issues being described by different users/teams in this thread:

  1. failed to fetch after a new deployment where the old version was deleted from the server
  • in this case, attempting to load the resource referenced by the error is expected to fail, it has been deleted
  1. failed to fetch that occurs somewhat randomly
  • in this case, attempting to load the resource succeeds

For people experiencing issue 1., the solution is to organise your deployments so that old versions aren’t deleted immediately. Doing a deployment of new app code doesn’t mean that all users running your app in their browser will update immediately (unless you force it somehow).

For those users who still have an older version running in their browser (lets say index.123.js), navigating to routes that load chunks dynamically will always reference the chunk that was current when they loaded your app (ie home.123.js), and this is correct. You don’t want the user getting half of their chunks with new app code and half with old, this would lead to very hard to debug issues. If you delete those chunks while the user is still navigating around the app, you will hit this error when they change routes and try to load a chunk they haven’t loaded yet.

For users that load the app after a deployment, index.html will reference a new index.456.js chunk that references all the other chunks that are consistent with that version (ie home.456.js if there are also changes in that chunk). The only difference is which version was referenced by index.html when the user loaded the tab.

If you keep a manifest of each deployment, and your instrumentation/logging includes the “version” of the app users are using, you should be able to figure out how long old versions are still used. My team has seen users still operating in the same tab they loaded a month ago without updating, so we keep all our built assets deployed for a long time. If you want to force users to reload when a new version is deployed, that’s a whole piece of engineering in itself.

While I understand that we should try to solve this problem ourselves, it seems like this must be quite a fundamental problem that developers will have when creating apps with vite? Especially when continuous deployment comes into the mix, because the files update very often and trigger lots of errors. For now I am back to “nightly deployments” to try and minimize disruption 😬.

It would be great if there was a standard solution somewhere in the docs. Preferably one without making the users manually “opt-in” to a page reload. Even nicer would be if vite could somehow handle it for us so we don’t have to do anything. Eg catch the error and refresh the files without a hard reload of the page. That would greatly improve the DX 🙂.

Thanks for all the great work. Vite is awesome.

@patak-dev considering #12084 is now in main with the release of 4.4, should this be marked as resolved, considering it provides the ability to catch this error and handle it? I presume Vite won’t be adding any kind of automatic resolution like Nuxt has anyways?

So after implementing work done in https://github.com/vitejs/vite/pull/12084 - I’ve hit this error personally again, confirming using vite preload hook does not work as a solution to this problem… @patak-dev could vite team take a look at this thread again, as to me it still feels like a high priority issue to investigate, as lots of people are struggling with this problem

We are just hitting this now too, but it’s not just when the user has a browser tab open. It can also happen much later, if the user returns to our app after we have deployed. We’re trying to work out if it’s caused by the index.html page being cached for too long, rather than an issue with the JS file hashes themselves. Maybe it helps someone who is experiencing this? My reasoning goes like this:

  • Let’s say you have index.html which references index.abc.js which dynamically imports moduleX.123.js.
  • You update your code and deploy it, so now you have index.def.js which dynamically imports moduleX.456.js. This should not cause any “module not found” issues because all the file hashes are new, even the entry index JS file.
  • However, if your index.html file has been cached for longer, it still contains the reference to index.abc.js, which dynamically imports the now deleted moduleX.123.js. Hence you get an error.

I’m still confirming this but I wondered if it matches anyone else’s experience/setup?

After noticing that errors relating to dynamic module imports sometimes fails for our users , without us being able to replicate the problem, we decided to simply not lazy load components. We believe that automatic reloading of the page is probably a worse user experience than a larger bundle being served to users. Reliability is more important than the initial load speed of the main bundle.

When we build our application, we upload every file to a bucket in AWS S3, where the files are kept indefinitely. None of the automatically built and hashed files has been deleted. Because of this, it is unclear if this is a problem with our application or the way we have set up and are serving our application, some implementations of import in browsers, a problem with the computer or operating system, a problem or bug withing Vue Router, Vite, Vue, a temporary network outage, advertisement- or other content-blocking, or perhaps something completely different.

On Tuesday, last week, we removed lazy loading of components in our router/index.ts, and have seen far fewer of these errors reported, so it is definitely caused by something related to the lazy loaded components. Just wanted to add our experience and knowledge of this issue so far.

We are experiencing the same issues with a vite+vue SSR application. we are experiencing thousands of those a day. also multiple days after deployment… we also saw this after our very first go-live, where no “previous version” was online. We are completely desperate since one week on how to fix this. Other strange thing is, sometimes we get this error logged in Sentry, and when we try to check if the file really is not available, we are able to access it. Others are not working

I think it’ll likely solve the dynamic module loading issue - at the risk of hitting JavaScript issues as there’s no way to force the cache to clear. So the end user could be running stale modules with new modules… which is fine if no interfaces have changed. But otherwise could cause runtime errors.

Exactly. At the very least it is as much of a “fix” as a painkiller at a death squad. And at worst, it’ll break the app completely in unpredictable ways.

The only necessary “fix” is to avoid the problem entirely by following best practices we’ve established over 20 years ago:

  1. fingerprint all asset names (the opposite of removing the hashes from the file names). That’s what Vite does by default and with good reason.
  2. never delete old assets - they must remain available forever (or to be precise, as long as the TTL of your cache headers). This is the problem source.

That’s it - there’s nothing more to it. It is a solved problem and a non issue if you just follow these two rules.

I think the entire thread exists due to how many deployments nowadays became container based instead of just using a CDN (or any kind of static file hosting) for the built app files. But container based deployments lose the temporality aspect of already deployed apps and their users. Thus, a deployment workflow which doesn’t preserve the previously deployed assets is fundamentally broken. Moreover, it isn’t something that Vite will ever be able to fix because it doesn’t deploy or serve your built code.

Serving static files from a container is not only 100% wasteful, but is actively harmful because it creates problems such as this one. Containers are great for building apps and running server side code. NOT for static assets.

If your setup is container based and you can’t switch to a CDN then the workaround is to copy all the old assets from all previous deployments (as part of the deployment) to the new container, so they’re still available alongside the newly built files. Your container size will grow with each deployment, but that’s a small price to pay for preventing the app from breaking.

@yakobe in my case, it’s not about outdated chunks. I’m getting this with existing files that is even worst to figure out the problem and try to fix it, especially because the only way I’m able to reproduce the issue it’s shutting down the node server (I have SSR app) 😕

Why is this pending triage from January? I’m getting production issues because of this, what’s the fix, other than removing lazy loading?

image

White page when navigating backwards through browser navigation buttons. (I don’t know if this affects it). In sentry these notifications come daily and it is not clear what to do about it.

vite 4.3.7 + vue 3

I am facing same issue with SvelteKit + Vite.

image

@IPWright83 https://dev.to/voodu/vue-3-pwa-service-worker-12di https://medium.com/@FezVrasta/service-worker-updates-and-error-handling-with-react-1a3730800e6a

And here is an example that I did some years ago

import { register } from "register-service-worker"

if (process.env.NODE_ENV === "production") {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready() {
      console.log(
        "App is being served from cache by a service worker.\n" +
          "For more details, visit https://goo.gl/AFskqB"
      )
    },
    registered() {
      console.log("Service worker has been registered.")
    },
    cached() {
      console.log("Content has been cached for offline use.")
    },
    updatefound() {
      console.log("New content is downloading.")
    },
    updated() {
      console.log("New content is available please refresh.")
      caches.keys().then(names => {
        for (let name of names) {
          console.log(name)
          caches.delete(name)
        }
      })
      setTimeout(() => {
        window.location.reload()
      }, 1000)
    },
    offline() {
      console.log(
        "No internet connection found. App is running in offline mode."
      )
    },
    error(error) {
      console.error("Error during service worker registration:", error)
    }
  })
}

And another one using Axios interceptors

axios.interceptors.response.use((resp) => {
  let v = resp.headers['vers'] || 'default'

  if (v !== localStorage.getItem('vers') && resp.config.method == 'get') {
    localStorage.setItem('vers', v)
    window.location.reload() // For new version, simply reload on any get
  }

  return Promise.resolve(resp)
})

@cpojer Your solution works for me, thanks for it! 👏. By the way, the talk you gavethis year in React Summit (Amsterdam) was awesome😁

Here some changes I did based on my needs, using your code as the base:

I’d to work on solving this issue today and I think it is the less invasive. However I did some changes. Here my variations:

  1. I do not want to pollute the URL with the timestamp, so instead of storing the last-reload timestamp in the url I do it in the sessionStorage.

  2. I already have implemented errors management in the app using React Error Boundary (https://react.dev/reference/react/useTransition#displaying-an-error-to-users-with-error-boundary) so I do not want React.Lazy to handle the error the 2nd time (when there is an error and after reloading, there is still an error).

Instead of returning the default module with the error handling like you did, I just throw the error so ErrorBoundary detects it and manages it like other errors in the app.

Here my code based on your code:

import type { ComponentType } from 'react';
import * as React from 'react';

export const lazy = (factory: () => Promise<{ default: ComponentType<any> }>) => {
    return React.lazy(() => factory().catch(importErrorHandler));
};

export function importErrorHandler(err:string): { default: ComponentType<any> } {

    // Get the last reload time from local storage and the current time
    const timeStr = sessionStorage.getItem('last-reload');
    const time = timeStr ? Number(timeStr) : null;
    const now = Date.now();

    // If the last reload time is more than 10 seconds ago, reload the page
    const isReloading = !time || time + 10_000 < now;
    if (isReloading) {
        console.log('New version for this module found. Reloading ...')
        sessionStorage.setItem('last-reload', String(now));
        window.location.reload();
        // Return an empty module so we do not see the error in the app before reloading
        return { default: () => null }
    }

    // We let ErrorBoundary handle the error
    throw new Error(err);
}

I moved it to a react-lazy-with-reload.ts file, so instead of using React.lazy to do the lazy thing, I use lazy from import { lazy } from ‘…/path/react-lazy-reload.ts’

Here a gist with the modifications: https://gist.github.com/ivanbtrujillo/005db52e4a8ef8fd1dc8f2db3b442d45

Or if it is not possible for whatever reasons, it would be great to have some examples/recipes in the docs to solve it for various frameworks etc

same in vite4.3.1

I solved this in Athena Crisis by wrapping React.lazy with an error handler: https://twitter.com/cpojer/status/1730141247900459166

For anyone looking, I decided to go ahead with @victorlmneves approach, which has proven to work well - while the error is still raised, it doesn’t break the UI, as the page is automatically refreshed and then redirected to the page the user was trying to navigate. This appears to be the best solution to the issue at this moment:

// Hard-reload the page when chunk load errors match the navigation error
const routerInstance = router();
routerInstance.onError((error, to) => {
  const errors = ['Failed to fetch dynamically imported module', 'Unable to preload CSS'];

  if (errors.some((e) => error.message.includes(e))) {
    window.location = to.fullPath;
  }
});

This worked in my case. Vite version: 4.4.9 build: { rollupOptions: { output: { entryFileNames: "[name].js", chunkFileNames: "[name].js", }, }, } , https://rollupjs.org/configuration-options/#output-chunkfilenames

you mean you solved this problem with that?

I think it’ll likely solve the dynamic module loading issue - at the risk of hitting JavaScript issues as there’s no way to force the cache to clear. So the end user could be running stale modules with new modules… which is fine if no interfaces have changed. But otherwise could cause runtime errors.

I enabled ssr and it fixed it for me.

// vite.config.ts

import { defineConfig } from "@solidjs/start/config";

export default defineConfig({ start: { ssr: true } });

Hello, at the moment I have a solution for the error: “Failed to fetch dynamically imported module” in React, using react-router-dom. It’s worth noting that I’m on version 6.16.0 and I’m using createBrowserRouter. It is necessary to create an ErrorPage component, which should be passed to the errorElement prop of the router. I’ll provide the code below.

It is necessary to use the useErrorRouter hook to capture the error, this is a react-router-dom hook.

code:

ErrorView or ErrorPage


  const ERROR_MESSAGE= "dynamically imported module"
  // this string must be exactly like this

  export default function ErrorView() {
  const error = useRouteError() as any

  const handleReaload = () => {
    window.location.reload()
  }

  console.log(error)
  if (
    error?.message &&
    (error.message as string)
      .toLocaleLowerCase()
      .includes(ERROR_MESSAGE.toLocaleLowerCase())
      // 
  ) {
    setTimeout(() => {
      handleReaload()
    }, 1000)
    // this timeout is for the returned component to be displayed for the assigned time, but you can skip it and return null if you only want to perform the reload.
    
    return <UpdateApp /> // put here your custom component error to replace "Failed to fetch dynamically imported module"
  }
  
  return <>Your base Error Page componente here...</>
}

Router

// IMPORTANT: don’t use lazy import from your ErrorView or ErrorPage

  import ErrorView from "@views/error/error_view"

  export const Router = createBrowserRouter([
  {
    path: "/",
    element: <PublicLayout />,
    errorElement: <ErrorView />, // use the ErrorView OR ErrorPage
    children: [
       {
          path: "your_path",
          element: <>Example Component</>,
       }
    ],
  }
])

i’m using this react-router-dom version

   {
     "dependencies": {
       "react-router-dom": "^6.16.0",
      }
   }

Note that, most likely, the first time you implement this, it may not work. However, from the second or third time onwards, it should work because you will have this code in the cache. In previous attempts, when this solution was not present in your source code, the error persisted. Currently, it is the best solution I could apply. Hopefully, it will be helpful for you.

“I’m sorry if my English is not good.”

@aethr I agree with suggesting going back to the previous working solution, is one possible way to resolve this. I guess the frustration of many here (myself included), is that even if this is one of the most reported issues for Vite, there hasn’t been much comment from Vite team on this, apart from at the very start.

I also imagine going back to webpack is not an option for many, depending on how invested they are in Vite at this point. It took me awhile to migrate from webpack and I’ve been running on Vite for years now, so doing all the work to migrate to Webpack again is likely very daunting for others in similar situations.

Anyhow, I am not asking for a “fix”, but making a closing comment / closing the issue / etc from the core team would be appreciated (especially if it’s not a Vite issue for sure). I imagine many, when seeing an open issue they experience, lead them to believe there could be a fix from the core team at some point in the future, and it just has been forgotten (at least that’s how I feel when seeing active discussions on long-standing issues).

I think it’ll likely solve the dynamic module loading issue - at the risk of hitting JavaScript issues as there’s no way to force the cache to clear. So the end user could be running stale modules with new modules… which is fine if no interfaces have changed. But otherwise could cause runtime errors.

Exactly. At the very least it is as much of a “fix” as a painkiller at a death squad. And at worst, it’ll break the app completely in unpredictable ways.

The only necessary “fix” is to avoid the problem entirely by following best practices we’ve established over 20 years ago:

  1. fingerprint all asset names (the opposite of removing the hashes from the file names). That’s what Vite does by default and with good reason.
  2. never delete old assets - they must remain available forever (or to be precise, as long as the TTL of your caching headers). This is the problem source.

That’s it. There’s nothing more to it. It is a solved problem and a non issue if you just follow these two rules.

I think the entire thread exists due to how many deployments nowadays became container based instead of just using a CDN (or any kind of static file hosting) for the built app files. Serving static files from a container is not only 100% wasteful, but is actively harmful because it creates problems such as this one. Containers are great for building apps and hosting server side code. NOT for static assets.

I think there’s more to it, as we leave the default hashing on within Vite, and don’t remove old assets (we’ve an S3 bucket with a lot of content) but still hit this issue.

Exaclty same problem here. React + Vite App with static bundled files within a caddy image deployed to k8s… Random users reporting blank page errors (‘failed to fetch dynamically imported module…’) after deployments because the browser returns an old index.html including an unexisting import

image Here you can see some funny things… the requested index.html (including a current timestamp with “&tstamp=xyz”) gets requested and returns a non-cached response with an imported bundle which does not exist. Im not getting it 🥴

I am seeing something suuuuper similar for a sveltekit + Vite app.

image

Basically what I am seeing is that app.b1450e6.js is the old hash and when I am sitting there refreshing, I am getting it back just fine.

However, as soon as I get the new app I am requesting app.b71b41c6.js which is 404ing. BUT, it seems like app.b1450e6.js (the old one) is ALSO getting requested. This is succeeding, but the app is not going to use it because it is the newer app that needs the new hashed app.

I am a bit confused on why the old file is also getting requested but from the screenshot, it seems like the old version is still around and the new one is not, but the requested html isn’t going to use the new one because that is not what its asking for like here:

image

**Interesting point I want to make clear. In the original issue, it is hypothesized that a new version of the app is deployed but user’s are requesting old versions that are no longer there. From my experience and debugging it is actually the reverse. As you can see in the screenshot above, it is the NEW file that is not quite there yet and the old file still succeeds. However, the html that is from the new app is not wanting those old files so it just blanks.

Stupid question but has anyone tried to adapt the caching headers on the webserver? For example like said in that article: https://medium.com/@pratheekhegde/setting-caching-headers-for-a-spa-in-nginx-eb2f75f52441

So the index file will never be cached and always requested new?

This wont work for users that have already fetched old index.html and have fetched some modules and in the mean time deployment occurs. I had caching headers configured, but that did not fully resolved this issue. You should understand that index.html refetching happens only on new page load or page refreshing, if user keeps window open for long enough for a deployment to occur - he will face this issue

This is not something specific to Vite and happens with Webpack, too.

https://mitchgavan.com/code-splitting-react-safely/ http://dimaip.github.io/2020/04/25/deploying-apps-with-code-splitting/

I don’t have any idea to solve this on a bundler side.

Looks like vite emits an error when it fails to load dynamic imports. Documentation here. Using this solution helped us to refresh when vite errored out.

Found this tweet related to the topic where a dev shows a solution when using React and asks for feedback where Evan You gives a reply https://twitter.com/cpojer/status/1730141247900459166?t=yT1x3GDgin4uVW0oh1-Guw&s=31

SOLUTION ✅ at least for me…

For those who faced with such an error during production build and you use ^4.4.9 version of Vite make sure to remove:

/* @vite-ignore */ comment inside of your await import() dynamic import ❌

Otherwise Vite just ignores this dynamic import line that actually causes an issue that leads to this error eventually Failed fetch of dynamically imported module. Because it skips the line and doesn’t even create chunks during production build.

I added this special comment at some point, because dynamic import didn’t work without it at all back then…

Now I investigated this PR and figured out it has been fixed, so this comment must be removed to make it work again

For the ones working with SPA and the error is just related to trying to fetch files that no longer exist after a deployment, why don’t you store a version on the localStorage, and on the initial load you check if the version matches the one you are serving and if not, you trigger a hot-reload?

I did it in the past using a service worker and it worked fine

Hopefully, someone from Vite team will look at this issue again, considering this is 4th most commented issue and one that’s been open for awhile. Not to mention the severity of its effects in production…

image

P.S. I do hope Vite does provide a more config-free way of handling this. The new event emit is helpful, but I wish Vite would handle this automatically, considering how many people suffer from this error

I agree

ad-blocker

same

@doutatsu I haven’t personally tried going back to webpack, so I’m not confident that is a solution.

Webpack (often used with babel) makes significant transformations to your code to get it to run in the browser environment. I wouldn’t be surprised if native import() statements were transpiled by webpack to use a custom implementation. vite, on the other hand (from what I understand), tries to use the browser APIs as designed. This specific error is the one that the browser will throw if a native import() statement fails.

But if people have just started experiencing this issue after moving to vite and they weren’t experiencing it with their previous build solution, regardless of whether vite is the root cause, going back to their previous working solution should be considered.

I think the whole issue could be solved by better error handling. I just need a way to show the user a message, something like “Your application is out of date, please refresh the page” and not throw an unhandled error for Sentry to send.

But frankly I don’t even know where to handle it in the tech stack. 😕 Should it be handled by Vite, Svelte, Routify, Vercel, or my code?

I am running into this with a Sveltekit app in an Azure app service. Everytime we deploy to prod we are getting reports of a “blank page” and we are seeing it in QA as well when we deploy. When we get the new code, we get 404s for some immutable files and about nothing displays other than some dev information coming from the outer +layout.svelte file. Anyone know of a way to fix in sveltekit using vite?

vite version > 4.0.4 window 7 / some win10 show this problem,I didn’t find a solution other than lowering the vite version

916390667eac7e13edcfa8b38799cad7_

@patak-dev considering #12084 is now in main with the release of 4.4, should this be marked as resolved, considering it provides the ability to catch this error and handle it? I presume Vite won’t be adding any kind of automatic resolution like Nuxt has anyways?

Another data point for folks trying to figure this out.

In an effort to fix this error, we recently updated our deployment system so that it keeps copies of the JS and CSS files for every deployment. That helped a lot – we went from 500+ times per day to about 50 times per day. But it’s still happening, and we’ve confirmed that it happens even for files that definitely exist.

(For folks considering a similar approach, we started uploading all of our assets to an S3 bucket. Then we put Bunny CDN in front of the bucket and load the assets from there.)

Our best guess currently is that sometimes loading the assets times out?

I don’t think it’s the same problem as their feature. This is completely a client only issue - where the chunk has changed and the client still trying to fetch the old one, using a now expired URL. It has nothing to do with backend changes

Same issue, the resource do exist, and can be opened in a new tab. When click Ontology button, I got the following error.

image

I feel the solution on this issue was solved here #5825 .

Although, I see how this solution makes to load twice the index.js 😕 . When loaded you get one index.js and index.js?v=[hash]

@rubjo @ryoji-sagara were you able to find a solution for this duplicate issue?

Having those errors in Sentry too. It’s not about hashes changes though, I’m assuming just slow networks because it happens mostly on mobile devices.

Is there an elegant way to intercept this error so I can show some message that there was error loading?

window.onunhandledrejection or window.onerror don’t catch this. Don’t want to touch console.error - when I reproduced this issue I didn’t have actually console errors (only Sentry event)

@victorlmneves yeah, that works for vue-router and is in the StackOverflow link I shared. Unfortunately we’re using react-router through various indirection connected-react-router & react-router-config. So even if it did expose an onError handler which I’ve been unable to find, we’d struggle calling it 😦

I had a wakeup call two years ago, can anyone give me insight on this? My husband was acting shady AF, our fire stick was acting up, my Alexa was hacked, my phones hacked, my computers hacked, and things got dangerous. Pretty sure he was somehow communicating with the fire stick, as well as using it to control the IR on our cameras. I’ve been putting the pieces together ever since, but I’m not a programmer. Just someone trying to stay safe. Thanks. 20230428_190842

@ionnikovv It worked in ours without further configuration; I just added it in the main. tsx. We are using “vite”: “4.5.2.”

I want to shine in on this: We had two issues with the chunking of vite/rollup. One like many others here was that the older files were not available anymore after a new deploy and some of the solutions in this thread helped us by forcing a refresh.

The second issue we had was that some users had an CORS issue, we were seeing Failed to fetch in our error reporting software but after testing with users it showed in the console that they got a CORS error (which don’t show up in error reporting). Which was actually not a cors issue but a request to an asset being blocked (see: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSDidNotSucceed).

The reason why it was blocked was because some adblocker, plugin and/or virus scanner was blocking these files. We changed the default chunk names from [name]-[hash].js in the rollup output options to chunk-[hash].js and that resolved the issue that they were being blocked. I think it was because the [name] was included in the chunk which was sometimes something like: component.styled-[hash].js which might have confused these programs. Changing those chunks to chunk-[hash].js worked.

Came across this while researching the issue. It seems to have solved mine at least, so maybe it might help some of you too: https://www.npmjs.com/package/vite-plugin-preload

For me, it was caused by AdGuard

image

Looks like vite emits an error when it fails to load dynamic imports. Documentation here. Using this solution helped us to refresh when vite errored out.

Did you try it ?

Yes. We had to use the above solution instead of the posted solution in this thread:

router.onError((error, to) => {
  if (error.message.includes('Failed to fetch dynamically imported module')) {
    window.location = to.fullPath
  }
})

Issue is that Firefox has a different error message than Chrome when failing to fetch dynamic, we kept running into “Loading module from “X.js” failed due to disallowed mime type”.

@vicodinvic1 Your solution worked for me

I’m using Vite + Vuejs and dynamically importing my Vuejs components, So here was my dynamic import function:

  const menuComponent = (menuComponent) => {
    return defineAsyncComponent({
        loader: () => import(/* @vite-ignore */ `./Menu_xxx/${menuComponent}.vue`),
        loadingComponent: LoadingComponent,
        delay: 200,
        errorComponent: ErrorComponent,
        timeout: 3000
    })
  } 

When upgrading from Vite 4.4.8 to 4.4.9 I noticed none of my dynamic components compiled and by switching back to 4.4.8 all of them compiled.

Now after removing /* @vite-ignore */ I have all components compiled again by Vite 4.4.9.

Thanks,

@aethr Considering one of the options is to return to webpack, what do they do that Vite doesn’t? Can’t Vite provide the same experience as webpack to avoid this issue?

Just curious, as I never looked into it when using webpack, so I don’t know what are the differences specifically.

@victorlmneves Ah, I see. Thanks for the explanation. We have a single-page application where we are defining all routes on our own, so we are quite free in choosing what to lazy load and what not to lazy load. It would be very interesting to hear about your experience, in case you give Webpack a try instead of Vite.

For anyone looking, I decided to go ahead with @victorlmneves approach, which has proven to work well - while the error is still raised, it doesn’t break the UI, as the page is automatically refreshed and then redirected to the page the user was trying to navigate. This appears to be the best solution to the issue at this moment:

// Hard-reload the page when chunk load errors match the navigation error
const routerInstance = router();
routerInstance.onError((error, to) => {
  const errors = ['Failed to fetch dynamically imported module', 'Unable to preload CSS'];

  if (errors.some((e) => error.message.includes(e))) {
    window.location = to.fullPath;
  }
});

How can I replicate this for react? @doutatsu and where do we use this router instance?

I also faced a same issue on production. I implemented the @doutatsu approach and works well.

This worked in my case. Vite version: 4.4.9 build: { rollupOptions: { output: { entryFileNames: "[name].js", chunkFileNames: "[name].js", }, }, } ,

https://rollupjs.org/configuration-options/#output-chunkfilenames

Exaclty same problem here. React + Vite App with static bundled files within a caddy image deployed to k8s… Random users reporting blank page errors (‘failed to fetch dynamically imported module…’) after deployments because the browser returns an old index.html including an unexisting import

image Here you can see some funny things… the requested index.html (including a current timestamp with “&tstamp=xyz”) gets requested and returns a non-cached response with an imported bundle which does not exist. Im not getting it 🥴

In this explicit case i’ve mixed up the console error… just ignore it. If there will be some new insights, i’ll let you know Although the discussed topic is not the issue in my case, i also had the same case as other users. I will share infos when i can reproduce again

When caching chunks on the client side, whether through service workers or other methods, users who haven’t downloaded a chunk from a previous build experience TypeError: Failed to fetch dynamically imported module when they request for that chunk and the chunk has changes and its hashed name has changed as well. One possible solution is to have the same name for chunks in every build.

    rollupOptions: {
        output: {
          chunkFileNames: 'assets/abc.js',
        }
    }
     // abc.js, abc2.js, abc3.js, ...

But this can cause problems if there are dependencies between the chunks and the user downloads the new chunk when using previous build. As far as I understand, there will always be some potential problems with new build deployments if files are cached.

the same issue

We are running into this issue with the latest vite + NGINX and Heroku.

location ~* \.(?:manifest|appcache|html?|xml|json)$ { add_header Cache-Control "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0"; expires 0; }

Tried adding that to my NGINX config to turn of caching of the HTML page but Sentry still reports the error every so often.

image

White page when navigating backwards through browser navigation buttons. (I don’t know if this affects it). In sentry these notifications come daily and it is not clear what to do about it.

vite 4.3.7 + vue 3

Yeah we see these errors still semi frequently, despite the fact we never delete files from the server either, so all the old hashed files should still be there. Not found a good way to debug yet though.

I do not know why but the same error comes in vite (I use quasar) Vite : boot error: TypeError: Failed to fetch dynamically imported module: http://localhost:9000/@fs/Users/sc/dev/my-chipinput-quasar-extension/src/boot/register-my-chipinput-component.js The above was the message in the browser console

The reason was that a subcomponent was not copied. The error message was totally missleading under vite.

Webpack : Doing the very exact situation in Webpack the error is Module not found: Can’t resolve imported dependency “src/services/shared/utils” Did you forget to install it? You can run: yarn add src/services/shared/utils

Which is right. And the browser does not start at all.

I live now with this error: I know when it comes one of the subcomponents has an import where the imported file is not present. The shown error is just the top most module. So normally I find it. I have it often because I have a migration project to pinia . So I made a new application with vite and copy peu a peu the modules and change them. So the error is some kind of routine now.

I also considered @AlonMiz 's article but I don’t think that would be helpful in our case because after each build the file names have a different chunk name (e.g. home.12dcs3.js becomes home.4wdf34.js). So, since home.12dcs3.js no longer exists on our servers, trying to fetch the same file with cache busting (home.12dcs3.js?t=1683870100127) would not work.

Regarding files that don’t exist when a new version is deployed, the right solution is to use CDN. this will help users that have stale bundles in their browsers and are going to new dynamic imported pages. eg. user navigates to Home.commit1.tsx but the deployed version is Home.commit2.tsx. you ALWAYS want the last few bundles to exist in the CDN, to support users that still have the previous deployed version on their browser. this practice will help you avoid “downtime” in the moment of deployment, not only on navigation.

Regarding the solution I suggested in my article, it’s not fully bullet proof, as it doesn’t handle nested dynamically imported, and it’s not supporting all browser (although there’s a way to do so https://github.com/fatso83/retry-dynamic-import)

Useful solution & more info: https://medium.com/@alonmiz1234/retry-dynamic-imports-with-react-lazy-c7755a7d557a thanks to @AlonMiz

@bogdanailincaipnt thanks for sharing my article. yes, that’s helpful in some cases, and we use it in production. but unfortunately, it seems that until browsers won’t have a native solution for this, we will have a hard time getting around these issues, as the tree of lazy import might be extended, and we can only retry the first file in the tree.

i’m getting this error when i deploy my site to github pages. however, when i deploy the same built code to netlify, it works just fine.

Similarly, running into issues when app is deployed to Cloudflare CDN. Also did not have this issue when app was deployed to Netlify.

I wonder if there are any differences in cache invalidation behavior between CDNs, and whether they are contributing to the issue?

@lmiller1990 according to some of the descriptions I saw on https://github.com/cypress-io/cypress/issues/25913 it looks more or less like the problem that I have where we aren’t able to reproduce it locally. But most likely, my case is a different scenario from the others as it’s an SSR + SPA and the dynamics imports are falling for existing chunks (we also have a few for no longer existing chunks after a deployment, but not worried based on the number of occurrences) As final notes, not sure if this can help to have more clues on the problem, locally as I said we never got those errors…unless we kill/stop the local server when the page is almost rendered on the browser, and that way we get the errors. On the other hand on the defineAsyncComponent I have tried this approach without success as it was executing the request 3 more times and even so, we got the error as the end it still failed the import

import { defineAsyncComponent } from 'vue';

export const useComponent = (name: string) => {
    return defineAsyncComponent({
        loader: () => import(`../components/${name}/${name}.vue`),
        onError(error, retry, fail, attempts) {
            if (error.message.match(/fetch/) && attempts <= 3) {
                retry();
            } else {
                fail();
            }
        }
    });
};

A lot of Cypress users run into this: https://github.com/cypress-io/cypress/issues/25913

I don’t think I can fix it on the Cypress end. I can spend time (several days of development resources) to patch Vite if needed, but it looks like the cause isn’t entirely clear.

Some observations:

  1. Happens mostly with large bundles - like MUI (Material UI).
  2. Especially happens on CI with Docker + many instances running (eg, race condition/resource issue)?

Any tips – maybe from someone more familiar with Vite - on debugging this?

I will inspect Cypress more first and keep the limitations in mind.