vitejs / vite

Next generation frontend tooling. It's fast!
http://vitejs.dev
MIT License
67.21k stars 6.04k forks source link

TypeError: Failed to fetch dynamically imported module #11804

Open IPWright83 opened 1 year ago

IPWright83 commented 1 year ago

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.

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.
victorlmneves commented 10 months ago

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

vicodinvic1 commented 10 months ago

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

Yeah, I found such code somewhere on stackoverflow, it just works. Actually I had something like that when used webpack back then

import React from 'react'

const Storage = window.localStorage
const PAGE_REFRESH_KEY = 'page-has-been-force-refreshed'

function lazyLoadPage (page) {
  return React.lazy(async () => {
    try {
      const Page = await import(/* @vite-ignore */`../pages/${page}.js`)

      Storage.setItem(PAGE_REFRESH_KEY, 'false')

      return Page
    } catch (error) {
      const pageHasAlreadyBeenForceRefreshed = JSON.parse(
        Storage.getItem(PAGE_REFRESH_KEY)
      ) || false

      if (!pageHasAlreadyBeenForceRefreshed) {
        Storage.setItem(PAGE_REFRESH_KEY, 'true')

        return window.location.reload()
      }

      throw error
    }
  })
}
Queen-esther01 commented 10 months ago

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?

Hey @EskelCz , I have handled this. Create an error boundary that wraps your entire app. In the error boundary component, you'll have access to the error. look for this message 'Failed to fetch dynamically imported module' and render a view asking users to refresh. here is a snippet of my current setup:

<Sentry.ErrorBoundary fallback={(error) => <ErrorBoundary error={error}/>}>
   <Routes>
      <Route path='test' element={<Test/>}/>
   <Routes/>
</Sentry.ErrorBoundary>
const ErrorBoundary = ({ error }:errorType) => {

    const refreshPage = () => {
        location.reload()
    }
    console.log(error)

    return (
        <div className='text-center h-screen flex items-center justify-center max-w-xs md:max-w-xl m-auto'>
            <div className='border-lightGray px-6 py-6 border rounded-md flex items-center justify-center flex-col'>
                {
                    error.error.message.includes('error loading dynamically imported module') || error.error.message.includes('Failed to fetch dynamically imported module') 
                    ?   <div>
                            <div className=''>
                                <MdError size={60} className='text-teal mx-auto'/>
                            </div>
                            <h2 className='text-xl lg:text-2xl font-semibold mt-10 text-center'>A new version is available🥳🎉</h2>
                            <p className='mt-3 mb-5 md:w-72 mx-auto'>Please refresh to update this application and get all recent changes made.</p>
                            <div className='mt-5 flex justify-center items-center gap-3'>
                                <Button onClick={refreshPage} className='items-start mt-5'>
                                    Refresh
                                </Button>
                            </div>
                        </div>
                    :   <div>
                            <div className=''>
                                <MdError size={60} className='text-red mx-auto'/>
                            </div>
                            <h2 className='text-xl lg:text-2xl font-semibold mt-10'>Something went wrong</h2>
                            <p className='mt-3 mb-5 md:w-72'>Please describe the issue you have just encountered to help us fix it. 😊</p>
                        </div>
                }
            </div>
        </div>
    )
}

export default ErrorBoundary
Queen-esther01 commented 10 months ago

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

This did not work for me and instead introduced very weird errors I have no way to debug coming from the build minified js files

Queen-esther01 commented 10 months ago

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?

Markussss commented 10 months ago

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.

victorlmneves commented 10 months ago

@Markussss You say that you see fewer errors, but you still get errors related to dynamic imports?

victorlmneves commented 10 months ago

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

This did not work for me and instead introduced very weird errors I have no way to debug coming from the build minified js files

@Queen-esther01 activate the sourcemaps, that way it's easier to debug

Markussss commented 10 months ago

@victorlmneves Yes, fewer. We are still using lazy loaded components other places in the application, so it's to be expected.

To give you some perspective: The last week, we saw this error reported three times. The week before that, we saw this reported 2200 times.

whenmoon commented 10 months ago

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?

This is breaking production for us every time we deploy and has been ever since switching from Webpack to Vite. Our current solution is unfortunately the one above since nothing else seems to quite do the job (although we use React and not Vue, so put this logic in the ErrorBoundary). Using the array of strings is good because it allows you to add errors for different browsers.

victorlmneves commented 10 months ago

@victorlmneves Yes, fewer. We are still using lazy loaded components other places in the application, so it's to be expected.

To give you some perspective: The last week, we saw this error reported three times. The week before that, we saw this reported 2200 times.

@Markussss in my case I cannot do much on that :/ My project has server-driven UI architecture, so we don't build pages, only components, and then the backend is the one that creates the pages using the components that we have built, also the routes are provided by the backend (we only have the routes for the error pages declared directly on the code. Then we have a single generic component responsible for rendering each one of the pages that is not lazy loaded, but all the components used for rendering the page are lazy loaded. But recently we also found that even having headers to not cache the HTML, it's being cached. :/

Maybe I need to give it a try to Webpack to see if I get better results as I already saw several people complaining about this problem when they moved from Webpack to Vite

Markussss commented 10 months ago

@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.

sbitenc commented 10 months ago

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

elektronik2k5 commented 10 months ago

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.

aethr commented 10 months ago

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:

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.

doutatsu commented 10 months ago

@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.

aethr commented 10 months ago

@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.

doutatsu commented 10 months ago

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

vicodinvic1 commented 10 months ago

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

cpojer commented 9 months ago

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

ivanbtrujillo commented 9 months ago

@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

malykhinvi commented 9 months ago

@aethr thanks for the complete summary of the current state!

We have found another workaround, that might be useful if having a modern chunk is not super important for you. In the nutshell the solution is this:

  1. Use @vitejs/plugin-legacy and turn off modern chunks completely.
  2. Legacy plugin falls back to systemjs for dynamic imports
  3. And, a complete systemjs version supports onload hook and also provides an API to clear the cache failed script requests.
//vite.config.js
plugins: [
  legacy({
    renderModernChunks: false, // always use legacy chunks
    externalSystemJS: true, // do not use provided systemjs build because it does not support hooks and registry deletion API
    additionalLegacyPolyfills: ['systemjs/dist/system.min.js'], // provide a complete systemjs build
  })
]
// at the very beginning of the app entry point
if (window && window.System) {
  window.System.constructor.prototype.onload = function (isError: boolean, id: string) {
    // If the module fails to load, we want to be able to retry the loading
    // To do that, we want to remove the cached failed response from the SystemJS cache
    if (isError) {
      window.System.delete(id)
    }
  }
}

With this solution, we are able to retry the dynamic import after a network failure.

Ali-Javanmardi commented 9 months ago

@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,

ronilitman commented 9 months ago

@aethr thanks for the complete summary of the current state!

We have found another workaround, that might be useful if having a modern chunk is not super important for you. In the nutshell the solution is this:

  1. Use @vitejs/plugin-legacy and turn off modern chunks completely.
  2. Legacy plugin falls back to systemjs for dynamic imports
  3. And, a complete systemjs version supports onload hook and also provides an API to clear the cache failed script requests.
//vite.config.js
plugins: [
  legacy({
    renderModernChunks: false, // always use legacy chunks
    externalSystemJS: true, // do not use provided systemjs build because it does not support hooks and registry deletion API
    additionalLegacyPolyfills: ['systemjs/dist/system.min.js'], // provide a complete systemjs build
  })
]
// at the very beginning of the app entry point
if (window && window.System) {
  window.System.constructor.prototype.onload = function (isError: boolean, id: string) {
    // If the module fails to load, we want to be able to retry the loading
    // To do that, we want to remove the cached failed response from the SystemJS cache
    if (isError) {
      window.System.delete(id)
    }
  }
}

With this solution, we are able to retry the dynamic import after a network failure.

@malykhinvi besides having the ability to retry, do you think that using legacy chunks can "help" solve the issue? meaning - will I have less errors anyway?

malykhinvi commented 9 months ago

do you think that using legacy chunks can "help" solve the issue? meaning - will I have less errors anyway?

@ronilitman I believe so. With the default behaviour the error response is cached and every time you try to load a chunk there will be an error. With the workaround I proposed, the cache will be cleared after a chunk loading failure and the user will be able to successfully retry the chunk loading (for example, trigger the route change clicking the navigation link)

daiky00 commented 9 months ago

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?

lmiller1990 commented 9 months ago

@diaky00 I don't think so - the problem is there are many different underlying issues that manifest this error, and none of them have a good reproduction. I don't think there's any practical way anyone other than someone experiencing the bug in a consistent fashion can even try to fix it.

jack-hanlon commented 9 months ago

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

Is this possible with vite + react + firebase hosting. I am having basically the same error with React lazy on my dist build

victorlmneves commented 9 months ago

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

IraiDev commented 8 months ago

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."

muhammad0071 commented 7 months ago

@aethr thanks for the complete summary of the current state!

We have found another workaround, that might be useful if having a modern chunk is not super important for you. In the nutshell the solution is this:

  1. Use @vitejs/plugin-legacy and turn off modern chunks completely.
  2. Legacy plugin falls back to systemjs for dynamic imports
  3. And, a complete systemjs version supports onload hook and also provides an API to clear the cache failed script requests.
//vite.config.js
plugins: [
  legacy({
    renderModernChunks: false, // always use legacy chunks
    externalSystemJS: true, // do not use provided systemjs build because it does not support hooks and registry deletion API
    additionalLegacyPolyfills: ['systemjs/dist/system.min.js'], // provide a complete systemjs build
  })
]
// at the very beginning of the app entry point
if (window && window.System) {
  window.System.constructor.prototype.onload = function (isError: boolean, id: string) {
    // If the module fails to load, we want to be able to retry the loading
    // To do that, we want to remove the cached failed response from the SystemJS cache
    if (isError) {
      window.System.delete(id)
    }
  }
}

With this solution, we are able to retry the dynamic import after a network failure.

hey @malykhinvi i follow this guide but no luck, got errors like this, are that any other configs ? image

malykhinvi commented 7 months ago

@muhammad0071 you miss types for System js. The workaround is:

declare global {
  interface Window {
    System?: any
  }
}
cruzluna commented 7 months ago

I enabled ssr and it fixed it for me.

// vite.config.ts

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

export default defineConfig({ start: { ssr: true } });
smk87 commented 7 months ago

I was having this issue. After much searching and trial and error, I got it fixed by removing optimizeDeps from the vite config. Previously, it was like below. I just removed the entire optimizeDeps.

    optimizeDeps: {
      exclude: ['js-big-decimal'],
    }
dennis-gonzales commented 7 months ago

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

This is what ended up my solution too, many thanks!

Ma-An commented 7 months ago

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.

sergeushenecz commented 7 months ago

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 ?

Ma-An commented 7 months ago

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".

sergeushenecz commented 7 months ago

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".

Dou you call window.reaload also right?

ysyfff commented 6 months ago

@IPWright83 @victorlmneves If we can get the unique flag of the source file this problem may be solved, chunk.facadeModuleId seems to be the unique flag, unfortunately it may be null sometimes.

If someone can get the unique flag of source file, here is how to solve the problem:

  1. In generateBundle hook, rename fileName as [name]${uniqueFlag}-[hash].js
  2. rename bundled file name as [name]${uniqueFlag}.js
  3. in nginx, rewrite [name]${uniqueFlag}-[hash].js to [name]${uniqueFlag}.js

That's all, and I think it must be sure to solve this problem.

shkvaratskhelia commented 6 months ago

Run with sames error issue, finally tracked down the code and find out this kind of an import: import OptionFilter from '../../../components/utils/table/filter/OptionFilter.vue.vue' in one of the dependency component. For some reason (we are using GitHub copilot) we got this line and the error description did not reveal the cause at all. This might help someone )

lisonge commented 6 months ago

For me, it was caused by AdGuard

image

Jonesus commented 6 months ago

I was having this same issue when serving the built website with NGINX in Docker when running CI tests, accessing it with parallelized Playwright Chrome instances inside GitHub Actions. Due to unrelated constraints I had limited the net.ipv4.ip_local_port_range of sysctl to contain only 1000 ports, and when I increased the port range to 2000 ports, the issue didn't occur anymore.

derseitenschneider commented 6 months ago

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

ricklancee commented 5 months ago

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.

mrsudarshanrai commented 5 months ago

We were also facing the same issue after the build. We are using the vite event to track errors and force reloading while there are errors.

const swapDynamicLoadErrorPage = () => {
  if (document) {
    const rootElement = document.getElementById('root') as HTMLDivElement;
    if (rootElement) {
      rootElement.innerHTML = `<div class="redirect_screen">
      <h4><strong>New update available.</strong></h4>
      <p>Reloading in progress. Thanks for your patience.</p>
    </div>`;
    }
  }
};

/* catch preload event */
window.addEventListener('vite:preloadError', () => {
  window.location.reload();
  swapDynamicLoadErrorPage();
});

https://vitejs.dev/guide/build#load-error-handling

ionnikovv commented 5 months ago

Hi @mrsudarshanrai! I have a small question: I tried to use the same workaround you provided, but in my case, the vite:preloadError event is not being triggered. I placed it at the top of my index.jsx file, right after all imports. I'm curious if there are any additional hints for its usage. Thanks!

mrsudarshanrai commented 5 months ago

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

videmann commented 4 months ago

In case of this:

  1. add DEPLOY_DATE variable to .env with dateTime of deploy (update it with CI/CD software before deploy)
  2. Use fixed chunk names with parameter like "/components/[filename].[ext]?modified=" + process.env.DEPLOY_DATE

This method decides 2 problems:

  1. Still using browser cache (between deploys)
  2. using simple filenames without hash and solve problem "failed to fetch dynamically imported module" because filenames are still the same
TripSonny commented 4 months ago

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