Skip to content

TypeError: Failed to fetch dynamically imported module #11804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
IPWright83 opened this issue Jan 24, 2023 · 218 comments
Open

TypeError: Failed to fetch dynamically imported module #11804

IPWright83 opened this issue Jan 24, 2023 · 218 comments

Comments

@IPWright83
Copy link
Contributor

IPWright83 commented Jan 24, 2023

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.
@sapphi-red
Copy link
Member

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.

@awacode21
Copy link

awacode21 commented Jan 24, 2023

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

@IPWright83
Copy link
Contributor Author

Thanks @sapphi-red, is it Vite or Rollup generating these hashes (assuming that's what they are) on the filenames? I wonder if there's a way to suppress that behaviour and if it's required for cache busting whether there's any alternative approaches.

@victorlmneves
Copy link

victorlmneves commented Jan 24, 2023

@sapphi-red both generate hashes when running the build. If your product it's a SPA you can try to use a service worker together with an interceptor (if you use Axios) to manage that. I did it on Vue 2 where it's basically stores on the localStorage a version of the app and then compares if the one that you access it's the same or not and if not it triggers a reload of the browser in order to get the updated version

@IPWright83
Copy link
Contributor Author

@victorlmneves could you provide a bit of a fuller explanation/ code snippets maybe of tha so I understand the concepts a bit more? I'd be quite interested in exploring what that might look like.

@victorlmneves
Copy link

@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)
})

@sapphi-red
Copy link
Member

@IPWright83
You can remove these hashes from the filename by configuring entryFileNames and chunkFileNames.

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      entryFileNames: '[name].js',
      chunkFileNames: '[name].js'
    }
  }
})

But yes this is recommended for cache busting.

I don't know any alternatives other than the ones described in the articles I linked above.

@victorlmneves
Copy link

@IPWright83 by the way, have you tried to access directly the file that is displayed on the console that wasn't possible to import? I'm asking because on our project we get those errors randomly but when we access them directly the file exist

@IPWright83
Copy link
Contributor Author

I have @victorlmneves. Is there any way to adopt an approach more like this #5825 @sapphi-red? Anything after the queryString can still cache-bust, but as the file name is constant you'll get the new payload. I think that example is a little primitive (I think JS needs transforming too). Could you see any issues with this approach?

@sapphi-red
Copy link
Member

I guess it's possbile by using a plugin.
I think that will lead to runtime errors sometimes because the dynamic import in old version files can resolve to new version files.

For example,
first version deployed

// foo.js
export const foo = 'foo'
export const bar = 'bar'

// a file depends on foo.js
import('./foo.js').then(mod => {
  if (mod.bar.startsWith('b')) {
    console.log(mod.foo)
  }
})

second version deployed

// foo.js
export const foo = 'foo'

// a file depends on foo.js
import('./foo.js').then(mod => {
  console.log(mod.foo)
})

@victorlmneves
Copy link

@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
  }
})

@IPWright83
Copy link
Contributor Author

@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 :(

@mikemklee
Copy link

Any updates on this?
Also running into the same issue with a vue 2 + vite app, when using dynamic imports.

The vue router hack could work for some imports, but we have dynamic imports outside of the router, which i don't think the hack would be able to cover.

Besides, forcing a reload because an error occurred doesn't seem like the best UX, so wondering if there's a solution/workaround that is more subtle and "behind the scenes" 🤔

@yakobe
Copy link

yakobe commented Feb 1, 2023

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.

@victorlmneves
Copy link

@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) :/

SimonBin added a commit to AKSW/jena that referenced this issue Feb 7, 2023
This reverts commit b1fc7bf.

Workaround for vitejs/vite#11804
@lzacharkow
Copy link

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

@lmiller1990
Copy link

lmiller1990 commented Feb 27, 2023

A lot of Cypress users run into this: cypress-io/cypress#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.

@benadamstyles
Copy link

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?

@AlfonsoUceda
Copy link

Happened to me after upgrading from vite 4.0.1 to 4.1.4

@adamkpurdy
Copy link

@benadamstyles I had the same issue. I removed the hashing option in the config like another user mentioned.

@aakash14goplani
Copy link

aakash14goplani commented Mar 4, 2023

I am facing same issue with SvelteKit + Vite.

image

@eifr-vault
Copy link

Not sure if related but I'm getting the same error when using storybook with a react component that imports a module from an index.ts file (exports all others)

@namu254
Copy link

namu254 commented Mar 8, 2023

ad-blocker

same

@lmiller1990
Copy link

I reproduced this in CI only, you can follow my progress as I debug it in Cypress: cypress-io/cypress#25913 (comment) I suspect the same core issue. I think it's a race condition or resources related issue.

@aethr
Copy link

aethr commented Mar 9, 2023

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.

@lmiller1990
Copy link

According to quite a bit of research in cypress-io/cypress#25913 at least one class of error is related to CI on GH Actions specifically - not sure if this helps anyone, but worth noting in case it does.

@feelings93
Copy link

@liamvdv Excellent explanation! Currently, I use AWS S3 to store the React build dist folder. Instead of retaining the old files, I delete everything and upload the entire new dist folder, which leads to issues with failed dynamic imports. I’m considering modifying the upload step to keep the old files. However, I’m concerned that over time, the S3 bucket size will grow significantly. To address this, I plan to delete files older than 30 days each time I upload to S3. That said, I worry this approach might increase build time. What’s your opinion on this strategy?

@liamvdv
Copy link

liamvdv commented Dec 7, 2024

@feelings93 Sounds great, assets are usually sub 1k files, i.e. a single bucket list should be sufficient. It won’t increase build time and will require a few seconds more at deploy time.

That being said, I would like to challenge the deleting alltogether.

S3 costs 3cts/GB/month. If you have bucket versioning enabled, you’ll pay for them anyway — don’t bother deleting.

Most asset bundles I’ve worked with are max 4 MB regularly changing files (index.js bundle f.e.). Assuming you do 10 deploys a day, 20 workdays, thats 200 deploys a month. Coming in at just below 1 GB/month. You’ll pay 3cts more every month active. Don’t bother - especially since you are likely deploying (1) smaller changes, (2) less frequently.

Liam
CTO - botBrains.io

@ByteTerminator
Copy link

image
I don't know how to solve the warnings and errors that occur on Android devices when using Cordova

@wuchaoxin
Copy link

This issue has also encountered by me, for the production environment, you need to keep the previous release content. You cannot use new release content to overwrite the old content, otherwise, your users may still have the previous cache.

For the development environment, you need to add the following code to the routing:

router.onError((error, to) => {
    // NOTE: https://github.com/vitejs/vite/issues/11804
    if (
      error.message.includes('Failed to fetch dynamically imported module') ||
      error.type === 'loading-chunk'
    ) {
      console.warn(`Dynamic module failed to load: ${error.message}`);
      if (!window._reloadOnce) {
        window._reloadOnce = true;
        window.location.reload();
      }
    } else {
      console.error('Router error:', error);
    }
  });

@vrymel
Copy link

vrymel commented Dec 29, 2024

I had this same error in Vue 3. I nuked node_modules and did an npm install. That fixed the issue.

Not sure what caused it.

@1361176289
Copy link

I had this same error in Vue 3. I nuked node_modules and did an npm install. That fixed the issue.

Not sure what caused it.
I encountered it in the production environment. I haven't encountered it in the local environment. I always thought it was my code. I didn't see it until today.

@hitotch
Copy link

hitotch commented Jan 21, 2025

@vrymel Same as your fix. Thank you.
rm -rf node_modules & npm install

@Tasin5541
Copy link

The vite:preloadError solution mentioned in the doc does reload the page but even with event.preventDefault() the error boundary in react catches the error first and shows the error page just before the reload.

I have added this event listener at the top of main.tsx file, right after the imports.

@XXXmez
Copy link

XXXmez commented Feb 11, 2025

Will there ever be a proper solution to this issue?
We are encountering this problem and haven’t found any reliable way to handle it. Additionally, we noticed that after the first occurrence of the 'vite:preloadError' error, if we try to load the same page again without reloading or refreshing the application, it crashes with a different error because React Router believes the page has been loaded.

@alessioacella
Copy link

Same here, sentry is getting full of errors

@MRezaSafari
Copy link

Will there ever be a proper solution to this issue? We are encountering this problem and haven’t found any reliable way to handle it. Additionally, we noticed that after the first occurrence of the 'vite:preloadError' error, if we try to load the same page again without reloading or refreshing the application, it crashes with a different error because React Router believes the page has been loaded.

already mentioned

#11804 (comment)

@awixor
Copy link

awixor commented Mar 12, 2025

I was able to reproduce this error randomly while opening my app in Microsoft Edge.

Issue

I had a file named StatsCounter.tsx, which was used inside a lazy-loaded component. My ad blocker blocked this file, causing the page to crash with the following errors:

  • TypeError: Failed to fetch dynamically imported module
  • .../StatsCounter/StatsCounter.tsx net::ERR_BLOCKED_BY_CLIENT

Solution

Renaming the StatsCounter file to something else solved the issue.

Key Finding

It seems that when an ad blocker blocks a file inside a lazy-loaded component, the app crashes instead of handling the missing module gracefully. This could be one of the causes of this issue.

For anyone with same issue

If you're facing a similar issue, check your error tracking service (Sentry, Airbrake, GlitchTip, etc.) for occurrences of ::ERR_BLOCKED_BY_CLIENT. This might help identify blocked resources causing crashes.

@mr-burhanuddin
Copy link

mr-burhanuddin commented Mar 25, 2025

Worked For Me:

import React from 'react';

export const lazyWithRetries = <T extends React.ComponentType<any>>(
  importer: () => Promise<{ default: T }>
): React.LazyExoticComponent<T> => {
  const retryImport = async () => {
    const RETRY_COUNT = 5;
    const RETRY_DELAY = 500;

    try {
      return await importer();
    } catch (error) {
      for (let attempt = 1; attempt <= RETRY_COUNT; attempt++) {
        await new Promise((resolve) =>
          setTimeout(resolve, RETRY_DELAY * 2 ** attempt)
        );

        try {
          const bust = `bust=${Date.now()}-${attempt}`;
          const modulePath =
            error instanceof Error
              ? error.message
                  .replace(
                    /^Failed to fetch dynamically imported module:\s*/i,
                    ''
                  )
                  .split(/[\n'"]/)[0]
              : null;

          if (!modulePath) throw error;

          return await import(
            /* @vite-ignore */
            `${modulePath}${modulePath.includes('?') ? '&' : '?'}${bust}`
          );
        } catch (retryError) {
          console.warn(`Chunk load retry ${attempt} failed:`, retryError);
          if (attempt === RETRY_COUNT) {
            window.location.reload();
          }
        }
      }

      throw error;
    }
  };

  return React.lazy(() =>
    retryImport().catch((error) => ({
      default: () => (
        <div
          style={{
            padding: '2rem',
            textAlign: 'center',
            height: '100dvh',
            width: '100%',
          }}
        >
          <h2>Something went wrong</h2>
          <p>We are trying to reload the application...</p>
          <button onClick={() => window.location.reload()}>Reload Now</button>
          <details style={{ marginTop: '1rem' }}>
            <summary>Technical details</summary>
            <pre style={{ whiteSpace: 'pre-wrap', textAlign: 'left' }}>
              {String(error)}
            </pre>
          </details>
        </div>
      ),
    }))
  );
};

@juanporley
Copy link

For context, at our company we migrated to Vite (react project) and are seeing these issues.

  • We do not delete older assets at all and we prefix every path (public URL) with the specific version, so trying to load an older asset from a newer version is not really a use case for us.
  • When we see these issues, the files that allegedly cannot be loaded load without issues when manually accessing them.
  • The issue happens momentarily for a user and then it gets fixed quickly, presumably after a reload.
  • We'll be introducing the lazy retry logic mostly to try to gather more information about these failures, since they're not exactly what's described in some of the posts above.

@oleg-rdk
Copy link

#19695 feels to be related

@zuhaib10
Copy link

We were facing a strange issue during development where the app would occasionally break or fail to load properly. It turned out to be related to Vite’s preload behavior.
window.addEventListener('vite:preloadError', (event) => { window.location.reload(); });

Might be helpful .

@MathiasWP
Copy link

We encounter this problem, but only in our deployed version of the app: sveltejs/kit#13655

The issue seems to be related to the browser's native behaviour of failed dynamic imports. If a dynamic import fails (e.g. due to no internet connection), then the browser will save the failed response even though the user tries again. Is this something Vite could add a workaround for? It is kind of crucial for the stability of an application that the app works again after a loss of internet connection.

@alessioacella
Copy link

alessioacella commented Mar 31, 2025

Same here using nuxt3

@majid-amiri
Copy link

Something really weird is happening with this.

We have user monitoring in place and we still see this error after implementing retries with cache busting. Also exact file that fails to load with Failed to fetch dynamically imported module loads perfectly after window.location.reload. We know this for sure because we are logging the exact file name:

File name from error:

Screenshot 2024-05-16 at 10 40 41 Network request that was successfull: Screenshot 2024-05-16 at 10 43 15 No matter to successful network request - error still being thrown.

Hey there,
This is happening for us too. After a few hours of investigations, I noticed that the chunk itself is being loaded successfully, but one of the chunks that it's using and importing "inside" has some 4xx issues.

Let's say:

// chunk-a.js
import b from "chunk-b.js"

chunk-a.js is loaded successfully without issues, but not chunk-b.js, and therefore chunk-a.js has status code 200, but in fact it fails because it cannot execute the code, and that's why the error is reported for chunk-a.js, and not chunk-b.js.

Anyone else facing this issue? Any workarounds on that?

@alessioacella
Copy link

Any news on this?

@MathiasWP
Copy link

Something really weird is happening with this.
We have user monitoring in place and we still see this error after implementing retries with cache busting. Also exact file that fails to load with Failed to fetch dynamically imported module loads perfectly after window.location.reload. We know this for sure because we are logging the exact file name:
File name from error:
Screenshot 2024-05-16 at 10 40 41
Network request that was successfull:
Screenshot 2024-05-16 at 10 43 15
No matter to successful network request - error still being thrown.

Hey there, This is happening for us too. After a few hours of investigations, I noticed that the chunk itself is being loaded successfully, but one of the chunks that it's using and importing "inside" has some 4xx issues.

Let's say:

// chunk-a.js
import b from "chunk-b.js"

chunk-a.js is loaded successfully without issues, but not chunk-b.js, and therefore chunk-a.js has status code 200, but in fact it fails because it cannot execute the code, and that's why the error is reported for chunk-a.js, and not chunk-b.js.

Anyone else facing this issue? Any workarounds on that?

This is a problem with the browsers native behaviour for dynamic imports. If you fetch any dynamic module chunk-*.js, and it fails due to e.g. bad internet connection, then the browser will cache that the import failed and always return an error, even when you try to re-import the module with a stable internet connection. For us (using SvelteKit) the only solution we currently have is to bundle everything inline and avoid importing our modules dynamically at all. This drastically impacts our initial load time, and it would at least be better to have an option to split the bundles into separate normal "script" tags and then fetch multiple chunks at the same time, but SvelteKit (and Vite AFAI) does not support for this. But we have concluded that it's better to have a slower initial load time and in return make our app load as expected. After we changed to inline bundling, all of our "failed to fetch dynamically imported module" errors have disappeared.

Also note that having Skew Protection (if you're using Vercel) can reduce the chance of these errors occurring, but it does not solve the root problem here.

I feel that this is an important problem to address, because it's clear that importing scripts via type="module" is not as great as it should be.

@majid-amiri
Copy link

Something really weird is happening with this.
We have user monitoring in place and we still see this error after implementing retries with cache busting. Also exact file that fails to load with Failed to fetch dynamically imported module loads perfectly after window.location.reload. We know this for sure because we are logging the exact file name:
File name from error:
Screenshot 2024-05-16 at 10 40 41
Network request that was successfull:
Screenshot 2024-05-16 at 10 43 15
No matter to successful network request - error still being thrown.

Hey there, This is happening for us too. After a few hours of investigations, I noticed that the chunk itself is being loaded successfully, but one of the chunks that it's using and importing "inside" has some 4xx issues.
Let's say:

// chunk-a.js
import b from "chunk-b.js"

chunk-a.js is loaded successfully without issues, but not chunk-b.js, and therefore chunk-a.js has status code 200, but in fact it fails because it cannot execute the code, and that's why the error is reported for chunk-a.js, and not chunk-b.js.
Anyone else facing this issue? Any workarounds on that?

This is a problem with the browsers native behaviour for dynamic imports. If you fetch any dynamic module chunk-*.js, and it fails due to e.g. bad internet connection, then the browser will cache that the import failed and always return an error, even when you try to re-import the module with a stable internet connection. For us (using SvelteKit) the only solution we currently have is to bundle everything inline and avoid importing our modules dynamically at all. This drastically impacts our initial load time, and it would at least be better to have an option to split the bundles into separate normal "script" tags and then fetch multiple chunks at the same time, but SvelteKit (and Vite AFAI) does not support for this. But we have concluded that it's better to have a slower initial load time and in return make our app load as expected. After we changed to inline bundling, all of our "failed to fetch dynamically imported module" errors have disappeared.

Also note that having Skew Protection (if you're using Vercel) can reduce the chance of these errors occurring, but it does not solve the root problem here.

I feel that this is an important problem to address, because it's clear that importing scripts via type="module" is not as great as it should be.

Hello @MathiasWP and thanks for your detailed explanation.
May I ask how did you end up having inline assets? Actually we are using Vite + Preact, but maybe your vite.config.ts can provide some insight on that.
Thanks in advance

@MathiasWP
Copy link

Something really weird is happening with this.
We have user monitoring in place and we still see this error after implementing retries with cache busting. Also exact file that fails to load with Failed to fetch dynamically imported module loads perfectly after window.location.reload. We know this for sure because we are logging the exact file name:
File name from error:
Screenshot 2024-05-16 at 10 40 41
Network request that was successfull:
Screenshot 2024-05-16 at 10 43 15
No matter to successful network request - error still being thrown.

Hey there, This is happening for us too. After a few hours of investigations, I noticed that the chunk itself is being loaded successfully, but one of the chunks that it's using and importing "inside" has some 4xx issues.
Let's say:

// chunk-a.js
import b from "chunk-b.js"

chunk-a.js is loaded successfully without issues, but not chunk-b.js, and therefore chunk-a.js has status code 200, but in fact it fails because it cannot execute the code, and that's why the error is reported for chunk-a.js, and not chunk-b.js.
Anyone else facing this issue? Any workarounds on that?

This is a problem with the browsers native behaviour for dynamic imports. If you fetch any dynamic module chunk-*.js, and it fails due to e.g. bad internet connection, then the browser will cache that the import failed and always return an error, even when you try to re-import the module with a stable internet connection. For us (using SvelteKit) the only solution we currently have is to bundle everything inline and avoid importing our modules dynamically at all. This drastically impacts our initial load time, and it would at least be better to have an option to split the bundles into separate normal "script" tags and then fetch multiple chunks at the same time, but SvelteKit (and Vite AFAI) does not support for this. But we have concluded that it's better to have a slower initial load time and in return make our app load as expected. After we changed to inline bundling, all of our "failed to fetch dynamically imported module" errors have disappeared.
Also note that having Skew Protection (if you're using Vercel) can reduce the chance of these errors occurring, but it does not solve the root problem here.
I feel that this is an important problem to address, because it's clear that importing scripts via type="module" is not as great as it should be.

Hello @MathiasWP and thanks for your detailed explanation. May I ask how did you end up having inline assets? Actually we are using Vite + Preact, but maybe your vite.config.ts can provide some insight on that. Thanks in advance

This is an available config in the SvelteKit configuration:

Image

Not sure how you can achieve the same with other frameworks, but i would guess that there are ways to achieve this by using/writing a plugin or asking the maintainers if they have a solution,

@majid-amiri
Copy link

Something really weird is happening with this.
We have user monitoring in place and we still see this error after implementing retries with cache busting. Also exact file that fails to load with Failed to fetch dynamically imported module loads perfectly after window.location.reload. We know this for sure because we are logging the exact file name:
File name from error:
Screenshot 2024-05-16 at 10 40 41
Network request that was successfull:
Screenshot 2024-05-16 at 10 43 15
No matter to successful network request - error still being thrown.

Hey there, This is happening for us too. After a few hours of investigations, I noticed that the chunk itself is being loaded successfully, but one of the chunks that it's using and importing "inside" has some 4xx issues.
Let's say:

// chunk-a.js
import b from "chunk-b.js"

chunk-a.js is loaded successfully without issues, but not chunk-b.js, and therefore chunk-a.js has status code 200, but in fact it fails because it cannot execute the code, and that's why the error is reported for chunk-a.js, and not chunk-b.js.
Anyone else facing this issue? Any workarounds on that?

This is a problem with the browsers native behaviour for dynamic imports. If you fetch any dynamic module chunk-*.js, and it fails due to e.g. bad internet connection, then the browser will cache that the import failed and always return an error, even when you try to re-import the module with a stable internet connection. For us (using SvelteKit) the only solution we currently have is to bundle everything inline and avoid importing our modules dynamically at all. This drastically impacts our initial load time, and it would at least be better to have an option to split the bundles into separate normal "script" tags and then fetch multiple chunks at the same time, but SvelteKit (and Vite AFAI) does not support for this. But we have concluded that it's better to have a slower initial load time and in return make our app load as expected. After we changed to inline bundling, all of our "failed to fetch dynamically imported module" errors have disappeared.
Also note that having Skew Protection (if you're using Vercel) can reduce the chance of these errors occurring, but it does not solve the root problem here.
I feel that this is an important problem to address, because it's clear that importing scripts via type="module" is not as great as it should be.

Hello @MathiasWP and thanks for your detailed explanation. May I ask how did you end up having inline assets? Actually we are using Vite + Preact, but maybe your vite.config.ts can provide some insight on that. Thanks in advance

This is an available config in the SvelteKit configuration:

Image Not sure how you can achieve the same with other frameworks, but i would guess that there are ways to achieve this by using/writing a plugin or asking the maintainers if they have a solution,

Thanks @MathiasWP ! I applied some changes on our chunking strategy based on your hints, and now we are monitoring the impact. What we did:

  • Removed all "dynamic" or "lazy" imports in the codebase, which it automatically caused in generating less chunks
  • Added manual chunking strategy for vendor (node_modules)

So after our changes, the number of chunks went from ~30 to 4 chunks, and also no dynamic imports at all.

Now I'm wondering, if modulepreload can cause issues or is it safe to keep it as it is? In other words, is it safe to have regular/static javascript imports in the chunks? Becuase, even with new chunking strategy and removing dynamic imports, I can still see some loading errors in our Sentry even for new chunks. I'm wondering if this is still something Vite/App related, or is it server and deployment issues from now on?

@MathiasWP
Copy link

Something really weird is happening with this.
We have user monitoring in place and we still see this error after implementing retries with cache busting. Also exact file that fails to load with Failed to fetch dynamically imported module loads perfectly after window.location.reload. We know this for sure because we are logging the exact file name:
File name from error:
Screenshot 2024-05-16 at 10 40 41
Network request that was successfull:
Screenshot 2024-05-16 at 10 43 15
No matter to successful network request - error still being thrown.

Hey there, This is happening for us too. After a few hours of investigations, I noticed that the chunk itself is being loaded successfully, but one of the chunks that it's using and importing "inside" has some 4xx issues.
Let's say:

// chunk-a.js
import b from "chunk-b.js"

chunk-a.js is loaded successfully without issues, but not chunk-b.js, and therefore chunk-a.js has status code 200, but in fact it fails because it cannot execute the code, and that's why the error is reported for chunk-a.js, and not chunk-b.js.
Anyone else facing this issue? Any workarounds on that?

This is a problem with the browsers native behaviour for dynamic imports. If you fetch any dynamic module chunk-*.js, and it fails due to e.g. bad internet connection, then the browser will cache that the import failed and always return an error, even when you try to re-import the module with a stable internet connection. For us (using SvelteKit) the only solution we currently have is to bundle everything inline and avoid importing our modules dynamically at all. This drastically impacts our initial load time, and it would at least be better to have an option to split the bundles into separate normal "script" tags and then fetch multiple chunks at the same time, but SvelteKit (and Vite AFAI) does not support for this. But we have concluded that it's better to have a slower initial load time and in return make our app load as expected. After we changed to inline bundling, all of our "failed to fetch dynamically imported module" errors have disappeared.
Also note that having Skew Protection (if you're using Vercel) can reduce the chance of these errors occurring, but it does not solve the root problem here.
I feel that this is an important problem to address, because it's clear that importing scripts via type="module" is not as great as it should be.

Hello @MathiasWP and thanks for your detailed explanation. May I ask how did you end up having inline assets? Actually we are using Vite + Preact, but maybe your vite.config.ts can provide some insight on that. Thanks in advance

This is an available config in the SvelteKit configuration:
Image
Not sure how you can achieve the same with other frameworks, but i would guess that there are ways to achieve this by using/writing a plugin or asking the maintainers if they have a solution,

Thanks @MathiasWP ! I applied some changes on our chunking strategy based on your hints, and now we are monitoring the impact. What we did:

  • Removed all "dynamic" or "lazy" imports in the codebase, which it automatically caused in generating less chunks
  • Added manual chunking strategy for vendor (node_modules)

So after our changes, the number of chunks went from ~30 to 4 chunks, and also no dynamic imports at all.

Now I'm wondering, if modulepreload can cause issues or is it safe to keep it as it is? In other words, is it safe to have regular/static javascript imports in the chunks? Becuase, even with new chunking strategy and removing dynamic imports, I can still see some loading errors in our Sentry even for new chunks. I'm wondering if this is still something Vite/App related, or is it server and deployment issues from now on?

I am not quite sure how modulepreload affects this. Also, i just want to make it clear that doing this will affect the initial render time for end-users, as it sadly removes the benefits of lazy loading. IMO, this problem should be solved within Vite's bundling strategy, for example by bypassing cache of failed dynamic imports with a query parameter or some other method.

But i'm looking forward to hearing if this change decreased the amount of errors on your end! If it does, then it is definitely something Vite should make clear, as this is an important issue to address.

@elektronik2k5
Copy link

I agree with @MathiasWP:

IMO, this problem should be solved within Vite's bundling strategy, for example by bypassing cache of failed dynamic imports with a query parameter or some other method.

Absolutely, assuming that's possible.

@majid-amiri
Copy link

Something really weird is happening with this.
We have user monitoring in place and we still see this error after implementing retries with cache busting. Also exact file that fails to load with Failed to fetch dynamically imported module loads perfectly after window.location.reload. We know this for sure because we are logging the exact file name:
File name from error:
Screenshot 2024-05-16 at 10 40 41
Network request that was successfull:
Screenshot 2024-05-16 at 10 43 15
No matter to successful network request - error still being thrown.

Hey there, This is happening for us too. After a few hours of investigations, I noticed that the chunk itself is being loaded successfully, but one of the chunks that it's using and importing "inside" has some 4xx issues.
Let's say:

// chunk-a.js
import b from "chunk-b.js"

chunk-a.js is loaded successfully without issues, but not chunk-b.js, and therefore chunk-a.js has status code 200, but in fact it fails because it cannot execute the code, and that's why the error is reported for chunk-a.js, and not chunk-b.js.
Anyone else facing this issue? Any workarounds on that?

This is a problem with the browsers native behaviour for dynamic imports. If you fetch any dynamic module chunk-*.js, and it fails due to e.g. bad internet connection, then the browser will cache that the import failed and always return an error, even when you try to re-import the module with a stable internet connection. For us (using SvelteKit) the only solution we currently have is to bundle everything inline and avoid importing our modules dynamically at all. This drastically impacts our initial load time, and it would at least be better to have an option to split the bundles into separate normal "script" tags and then fetch multiple chunks at the same time, but SvelteKit (and Vite AFAI) does not support for this. But we have concluded that it's better to have a slower initial load time and in return make our app load as expected. After we changed to inline bundling, all of our "failed to fetch dynamically imported module" errors have disappeared.
Also note that having Skew Protection (if you're using Vercel) can reduce the chance of these errors occurring, but it does not solve the root problem here.
I feel that this is an important problem to address, because it's clear that importing scripts via type="module" is not as great as it should be.

Hello @MathiasWP and thanks for your detailed explanation. May I ask how did you end up having inline assets? Actually we are using Vite + Preact, but maybe your vite.config.ts can provide some insight on that. Thanks in advance

This is an available config in the SvelteKit configuration:
Image
Not sure how you can achieve the same with other frameworks, but i would guess that there are ways to achieve this by using/writing a plugin or asking the maintainers if they have a solution,

Thanks @MathiasWP ! I applied some changes on our chunking strategy based on your hints, and now we are monitoring the impact. What we did:

  • Removed all "dynamic" or "lazy" imports in the codebase, which it automatically caused in generating less chunks
  • Added manual chunking strategy for vendor (node_modules)

So after our changes, the number of chunks went from ~30 to 4 chunks, and also no dynamic imports at all.
Now I'm wondering, if modulepreload can cause issues or is it safe to keep it as it is? In other words, is it safe to have regular/static javascript imports in the chunks? Becuase, even with new chunking strategy and removing dynamic imports, I can still see some loading errors in our Sentry even for new chunks. I'm wondering if this is still something Vite/App related, or is it server and deployment issues from now on?

I am not quite sure how modulepreload affects this. Also, i just want to make it clear that doing this will affect the initial render time for end-users, as it sadly removes the benefits of lazy loading. IMO, this problem should be solved within Vite's bundling strategy, for example by bypassing cache of failed dynamic imports with a query parameter or some other method.

But i'm looking forward to hearing if this change decreased the amount of errors on your end! If it does, then it is definitely something Vite should make clear, as this is an important issue to address.

Thanks again @MathiasWP for your comment. I've started our global deployment last week and now monitoring the changes to see how/if it has any impact. I also agree with you, that this should be resolved by Vite.js team. We have other applications in our product line which use Webpack and none of them face this issue, even though they use dynamic imports and lazy loading. So if Vite.js cannot handle this asap, I have no choice other than migrating to Webpack.

@faisal-urrehman
Copy link

faisal-urrehman commented Apr 24, 2025

Vite’s mantra:

Redefining developer experience. Vite makes web development simple again.

My reality in production: Existing users refresh after a new deployment and are greeted with

Uncaught Error: Failed to fetch dynamically imported module → blank screen.

I’m struggling to call that simple or redefined. It’s costing our team real time and our users real frustration.

Each fast build trades off post‑deploy breakage—and that’s not a trade a modern DX tool should force on its users.

I believe in Vite’s goals, but right now this issue makes me question whether Vite is production‑ready for apps that can’t force a full‑page cache purge on every release.

Tried multiple work arounds including the one mentioned in the official docs but upon reload, the same error is thrown which triggers the reload again and app gets stuck in reload hell

window.addEventListener('vite:preloadError', (event) => {
  window.location.reload() // for example, refresh the page
})

Looking forward to real solutions, not just work‑arounds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests