5imone
5imone

Reputation: 433

Webpack code splitting: ChunkLoadError - Loading chunk X failed, but the chunk exists

I've integrated Sentry with my website a few days ago and I noticed that sometimes users receive this error in their console:

ChunkLoadError: Loading chunk <CHUNK_NAME> failed.
(error: <WEBSITE_PATH>/<CHUNK_NAME>-<CHUNK_HASH>.js)

So I investigated the issue around the web and discovered some similar cases, but related to missing chunks caused by release updates during a session or caching issues.

The main difference between these cases and mine is that the failed chunks are actually reachable from the browser, so the loading error does not depend on the after-release refresh of the chunk hashes but (I guess), from some network related issue. This assumption is reinforced by this stat: around 90% of the devices involved are mobile.

Finally, I come to the question: Should I manage the issue in some way (e. g. retrying the chunk loading if failed) or it's better to simply ignore it and let the user refresh manually?


2021.09.28 edit:

A month later, the issue is still occurring but I have not received any report from users, also I'm constantly recording user sessions with Hotjar but nothing relevant has been noticed so far.

I recently had a chat with Sentry support that helped me excluding the network related hypotesis:

Our React SDK does not have offline cache by default, when an error is captured it will be sent at that point. If the app is not able to connect to Sentry to send the event, it will be discarded and the SDK will no try to send it again.

Rodolfo from Sentry

I can confirm that the issue is quite unusual, I share with you another interesting stat: the user affected since the first occurrence are 882 out of 332.227 unique visitors (~0,26%), but I noticed that the 90% of the occurrences are from iOS (not generic mobile devices as I noticed a month ago), so if I calculate the same proportion with iOS users (794 (90% of 882) out of 128.444) we are near to a 0,62%. Still small but definitely more relevant on iOS.

Upvotes: 41

Views: 31305

Answers (4)

Towkir
Towkir

Reputation: 4014

TLDR:

Why is it happening? Cache
Can you solve it completely? No
Can you solve it partially? Yes
Should/Must you solve it? Not really


This happens most of the time because the browser caches the index.html (and a few other css and js) file of the webapp. Try turning off caching for the file by adding these to the nginx config:

location ~* \.html?$ {
  expires -1;
  add_header Pragma "no-cache";
  add_header Cache-Control "no-store, must-revalidate";
}

If you want to trigger a reload on the client side for this kind of errors, try this on your frontend app's router:

router.onError(error => {
  if (/loading chunk \d* failed./i.test(error.message)) {
    window.location.reload()
  }
})

Caution: possibility of infinite reload if reload does not resolve the issue

Credit: Read more technical steps here on this article that already explains a lot more than I planned to write. Learn more about different chunk errors here.


Facts

  • Not all chunk errors are of same type, or for same reason.
  • Reachable chunks does not mean they can be loaded. (reach !== load)
  • Sometimes developers confuse syntax errors with chunk errors.
  • Sometimes it can happen due to missing env variables.

My assumption on OPs description:

  • It always should happen after e production release, but you might get reports at random times, because not all the users visit within a close timeframe after a release.
  • It is mostly iOS, because iOS users don't usually clear their recent apps (or browser) and whenever they revisit, their session is mostly idle and tries to access the cache, other OS/Devices let's them clear the apps and they visit from a new session more often (compared to iOS).

Upvotes: 2

Stanislav Berkov
Stanislav Berkov

Reputation: 6287

I also had such chunk load issue. I have angular app that is hosted by aspnet 6.0 in IIS. index.html has cache-control max-age=0, chunk files have hash in name e.g. main.4591141fc2c0d492.js and cache-control set to max-age=2592000. Problem with chunk loading began when I added public (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#public) directive for chunk files. Problem was volatile. Usually it worked ok but time to time server returned empty files. Empty file is a bad chunk so caused ChunkLoadError. The problem got worse since these files were cached. Reloading the page did not fix the problem since broken (empty) files were cached. Users had to clean browser cache to fix the issue.

Removing public cache directive resolved the issue.

Upvotes: 1

colder
colder

Reputation: 744

The chunk is reachable doesn't mean the user's browser can parse it. For example, if the user's browser is old. But the chunk contains new syntax.

Webpack loads the chunk by jsonp. It insert <script> tag into <head>. If the js chunk file is downloaded but cannot parsed. A ChunkLoadError will be throw.

You can reproduce it by following these steps. Write an optional chain and don't compile it. Ensure it output to a chunk.

const obj = {};
obj.sub ??= {};

Open your app by chrome 79 or safari 13.0. The full error message looks like this:

SyntaxError: Unexpected token '?'           // 13.js:2
MAX RELOADS REACHED                         // chunk-load-handler.js:24
ChunkLoadError: Loading chunk 13 failed.    // trackConsoleError.js:25
(missing: http://example.com/13.js)

Upvotes: 13

morganney
morganney

Reputation: 13580

This is most likely happening because the browser is caching your app's main HTML file, like index.html which serves the webpack bundles and manifest.

First I would ensure your web server is sending the correct HTTP response headers to not cache the app's index.html file (let's assume it is called that). If you are using NGINX, you can set the appropriate headers like this:

location ~* ^.+.html$ {
  add_header Cache-Control "no-store max-age=0";
}

This file should be relatively small in size for a SPA, so it is ok to not cache this as long as you are caching all of the other assets the app needs like the JS and CSS, etc. You should be using content hashes on your JS bundles to support cache busting on those. With this in place visits to your site should always include the latest version of index.html with the latest assets including the latest webpack manifest which records the chunk names.

If you want to handle the Chunk Load Errors you could set up something like this:

import { ErrorBoundary } from '@sentry/react'

const App = (children) => {
  <ErrorBoundary
    fallback={({ error, resetError }) => {
      if (/ChunkLoadError/.test(error.name)) {
        // If this happens during a release you can show a new version alert
        return <NewVersionAlert />

        // If you are certain the chunk is on your web server or CDN
        // You can try reloading the page, but be careful of recursion
        // In case the chunk really is not available
        if (!localStorage.getItem('chunkErrorPageReloaded')) {
          localStorage.setItem('chunkErrorPageReloaded', true)
          window.location.reload()
        }
      }

      return <ExceptionRedirect resetError={resetError} />
  }}>
    {children}
  </ErrorBoundary>
}

If you do decide to reload the page I would present a message to the user beforehand.

Upvotes: 8

Related Questions