ChunkLoadError: Loading chunk [number] failed

Filed under Deployment failures

Most likely causeAbout 10 min · 6 steps

You deployed a new version, and visitors holding the old page are asking for JavaScript files from the previous build — files the new deployment replaced, so the server answers 404.

Confirm the pattern: right after a deploy
Add a reload-once safety net
Stop browsers caching your HTML

Fastest route

Give your AI this prompt

Copy it, replace the bracketed part with your error, and send it.

Preview the prompt
My site throws "ChunkLoadError: Loading chunk [number] failed" (or "Failed to fetch dynamically imported module"). The reports started right after a deploy and hit returning visitors; fresh visits work fine.

Here is my setup: [hosting platform, e.g. Vercel / Netlify / my own server], [framework, e.g. Next.js], and here is where my app starts up: [paste your root layout, _app, or main entry file].

Please: 1) confirm this is the stale-HTML-after-deploy pattern and say what evidence supports it, 2) add a chunk-error safety net that reloads the page exactly once — guarded by a sessionStorage flag so it can never loop — placed where it will catch errors from lazy-loaded routes, 3) check my caching setup and set HTML responses to no-cache (or a very short max-age) while keeping hashed static chunks on long immutable caching — show me the exact config or header changes for my host, 4) tell me whether my host can keep the previous build's assets available, and if so, how to enable it.

Constraints: do not remove code-splitting or lazy loading to dodge the problem, do not add an unconditional reload handler without the once-only guard, and list the files you changed.

Fix it yourself

Follow the repair

  1. Confirm the pattern before changing anything. Check when the errors started and who they hit. ChunkLoadError reports that spike right after a deployment and affect returning visitors — while you cannot reproduce on a fresh visit — are this bug, virtually every time. Note the timing in your error tracker; it is also how you will prove the fix worked.
  2. Understand why hard refresh "fixes" it — and why that is not a fix. A hard refresh throws away the cached HTML and fetches the current version, whose chunk names match the files on the server. Your users will never do this. They will see a broken page and leave. Everything below exists so the site heals itself before a human has to know anything happened.
  3. Add a reload-once safety net for chunk errors. Catch the failure globally and reload — exactly once. The flag in sessionStorage is what makes it safe:
    // When a chunk fails to load, reload ONCE for fresh HTML.
    window.addEventListener("error", (event) => {
      const msg = String(event.message || event.error || "");
      if (!msg.includes("ChunkLoadError") && !msg.includes("Loading chunk")) return;
      if (sessionStorage.getItem("chunk-reloaded")) return;  // never loop
      sessionStorage.setItem("chunk-reloaded", "1");
      window.location.reload();
    }, true);
    
    // Clear the flag once the app has loaded successfully,
    // so a future deploy gets its one reload too.
    The same idea inside a React error boundary — catch the error, check the flag, reload once — works if your app already has one. One reload fetches current HTML and current chunks; if it still fails, the flag stops the cycle and the real error surfaces instead of an infinite refresh.
  4. Fix the real cause: stop browsers caching the HTML. Chunks deserve long caching — their hashed names change when content does, so a cached chunk is never stale. HTML is the opposite: it is the index, and it must be fresh. Check the response headers on your page (not the chunk files): HTML should come back with Cache-Control: no-cache or a very short max-age, while static chunks can keep max-age=31536000, immutable. On most managed hosts the defaults are already right for framework builds — the problem usually arrives via a custom CDN rule or a "cache everything" setting that treats HTML like an image. Find it and exempt the HTML.
  5. If your host supports it, keep the previous build's files alive. Some platforms can retain the prior deployment's static assets for a grace period, so old HTML wandering in from a cache still finds its chunks while it ages out. If yours offers asset retention or skew protection, turn it on. If it does not — many atomic-deploy hosts simply replace everything — steps 3 and 4 are the complete fix on their own.
  6. Verify like a returning visitor, not a fresh one. Open your live site and keep the tab. Deploy a trivial change. Go back to the old tab and navigate around. Before the fix, this reproduces the error; after it, the worst case is one brief automatic reload. Then watch the error reports after your next real deploy — the post-deploy spike should be gone.
Why this happensOptional explanation

Your site's JavaScript does not ship as one big file. The bundler — webpack, under the hood of Next.js, Create React App, and most AI-builder exports — splits it into numbered or hashed pieces called chunks, so browsers only download the code each page needs. The HTML page acts as the index: it names the exact chunk files to fetch. Those names contain hashes of the file contents, so every time you change code and redeploy, the chunk names change too.

Here is the collision. A visitor loaded your site at 2 PM and kept the tab, or their browser cached the page. You deployed at 3 PM, and the new deployment deleted the old chunk files. At 4 PM the visitor clicks something, the old HTML asks for a chunk by its old name, and the server — quite correctly — says 404. The browser reports ChunkLoadError: Loading chunk N failed. Nothing is wrong with the visitor's code path or your new build; the index and the files belong to two different versions of your site. This is also why the error is invisible to you: your visits always fetch fresh HTML, so your index always matches your chunks.

  • Stale cached HTML: the browser kept the page from before the deploy, via a long-lived tab or an over-eager cache.
  • Old chunks deleted at deploy: most hosts replace the build output wholesale, so the previous version's files vanish instantly.
  • CDN serving a mix: an edge cache still holding old HTML while the origin already serves the new build — two versions live at once.
  • Genuine network failure (rare): a dropped connection mid-download raises the same error; it is random and rare, not clustered after deploys.

Quick answers

A hard refresh fixes it for me. Does that mean it is solved?

It proves the diagnosis: the page shell loaded, a chunk 404'd, and a fresh load fetched the current HTML pointing at chunks that exist. It also shows why the fix is about caching, not code — nothing about your app's logic changed between the broken and working attempts.

Could it just be a visitor's bad connection?

It can: a chunk that failed to download on a dropped connection raises the same error. The tell is pattern — one user once is network; many users right after a deploy is stale HTML. That is why the safety net reloads only once: a genuine network hiccup heals on reload, while a loop guard stops a persistent failure from refreshing forever.

Does this happen on non-Next.js sites too?

Yes — every framework that splits code into hashed chunks has the same physics: Vite, Create React App, Angular, plain webpack. The names differ (you may see 'Failed to fetch dynamically imported module'), but the stale-HTML-after-deploy mechanism and both fixes are identical.

One fix, every Friday.

One real error, explained in plain English, with the fix — in your inbox every Friday. No spam, no jargon, unsubscribe anytime.