-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
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
Comments
This is not something specific to Vite and happens with Webpack, too. https://mitchgavan.com/code-splitting-react-safely/ I don't have any idea to solve this on a bundler side. |
We are experiencing the same issues with a vite+vue SSR application. |
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. |
@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 |
@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. |
@IPWright83 And here is an example that I did some years ago
And another one using Axios interceptors
|
@IPWright83 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. |
@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 |
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? |
I guess it's possbile by using a plugin. For example, // 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)
}) |
@IPWright83
|
@victorlmneves yeah, that works for |
Any updates on this? 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" 🤔 |
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. |
@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) :/ |
This reverts commit b1fc7bf. Workaround for vitejs/vite#11804
For what it's worth, an ad-blocker was the root of this issue for me. Disabling it resolved my issue. |
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:
Any tips -- maybe from someone more familiar with Vite - on debugging this? I will inspect Cypress more first and keep the limitations in mind. |
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
I'm still confirming this but I wondered if it matches anyone else's experience/setup? |
Happened to me after upgrading from vite 4.0.1 to 4.1.4 |
@benadamstyles I had the same issue. I removed the hashing option in the config like another user mentioned. |
Not sure if related but I'm getting the same error when using storybook with a react component that imports a module from an |
same |
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. |
I think there are possibly two separate issues being described by different users/teams in this thread:
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 For users that load the app after a deployment, 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. |
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. |
@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? |
@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 |
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);
}
}); |
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. |
|
@vrymel Same as your fix. Thank you. |
The vite:preloadError solution mentioned in the doc does reload the page but even with I have added this event listener at the top of |
Will there ever be a proper solution to this issue? |
Same here, sentry is getting full of errors |
already mentioned |
I was able to reproduce this error randomly while opening my app in Microsoft Edge. IssueI had a file named
SolutionRenaming the Key FindingIt 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 issueIf you're facing a similar issue, check your error tracking service (Sentry, Airbrake, GlitchTip, etc.) for occurrences of |
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>
),
}))
);
}; |
For context, at our company we migrated to Vite (react project) and are seeing these issues.
|
#19695 feels to be related |
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. Might be helpful . |
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. |
Same here using nuxt3 |
Any news on this? |
Hello @MathiasWP and thanks for your detailed explanation. |
This is an available config in the SvelteKit configuration: ![]() 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:
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 |
I am not quite sure how 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. |
I agree with @MathiasWP:
Absolutely, assuming that's possible. |
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. |
Vite’s mantra:
My reality in production: Existing users refresh after a new deployment and are greeted with
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
Looking forward to real solutions, not just work‑arounds. |
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:
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
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 asetTimeout
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 believeReact.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:
If you were to reload the page, you can see that
Foo-b53985a6.js
has been renamed toFoo-535d5a10.js
(or similar new hash)System Info
The text was updated successfully, but these errors were encountered: