Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

rhand's avatar
Level 6

Post Deployment Lates Build ChunkLoadError: Loading chunk

I am getting errors post deployment of our Laravel Vue 2 application. It seems to be missing a chunk on loading causing the site to fail to load.

[Error] ChunkLoadError: Loading chunk 4634 failed.
(missing: https://restaurant.smtv.test/js/components/publish/page.js?id=896e468ee62e4a32)
    i (site.js:2:161383)
    (anonymous function) (site.js:2:162017)
    (anonymous function) (site.js:2:159374)
[Error] Unhandled Promise Rejection: ChunkLoadError: Loading chunk 4634 failed.
(missing: https://restaurant.smtv.test/js/components/publish/page.js?id=896e468ee62e4a32)
    (anonymous function) (site.js:2:109080)
Edit • Delete

on a production site that only is TypeError: undefined is not an object (evaluating 'r[t].call') so not very useful. We updated the Page.vue Vue component loading

export default {
    components: {
        SmtPublishedModule: () => import(/* webpackChunkName: "publish/smt-published-module" */ /* webpackPrefetch: true */ '../publish/PublishedModule.vue'),
        SmtFontClassGenerator: () => import(/* webpackChunkName:"publish/smt-font-class-generator" */ /* webpackPrefetch: true */  '../editor/FontPresetClassGenerator.vue'),
        SmtFontLoader: () => import(/* webpackChunkName:"publish/smt-font-loader" */ /* webpackPrefetch: true */  '../editor/FontLoader.vue'),
        SmtMiniCookies: () => import(/* webpackChunkName:"publish/smt-mini-cookies" */ /* webpackPrefetch: true */  '../MiniCookies.vue')
    },

We also added webpack prefetch to resources/js/published.js which is converted to site.js for published sites:

const PublishedPage = () => import(/* webpackChunkName:"publish/page"*/ /* webpackPrefetch: true */  './components/publish/Page.vue')

but that might streamline loading of components in resources/js/components/publish/Page.vue and resources/js/published.js` but does not solve the issue.

Post npm run prod and deployment of that code a disconnect between new code and published sites code occurs causing the site to fail. We do need to improve code / update Vue components every now and then and this should not cause the published sites to fail to load.

Here is part the webpack.mix.js

....
if (mix.inProduction()) {
  mix.webpackConfig({
    devtool: 'hidden-source-map', 
    plugins: [
      new CleanWebpackPlugin({
        cleanStaleWebpackAssets: false,
        protectWebpackAssets: true,
        cleanOnceBeforeBuildPatterns: ['js/components/*'],
      }),
      new BugsnagSourceMapUploaderPlugin({
        apiKey: '32a21ca56ec7f76bfc82ee3e648db1ec',
        appVersion: process.env.MIX_BUGSNAG_APP_VERSION,
        overwrite: true,
        publicPath: '*'
      })
    ],

    output: {
      chunkFilename: 'js/components/[name].js?id=[contenthash]',
    },
  })
} else {
  mix.webpackConfig({
    output: {
      chunkFilename: 'js/components/[name].js',
    },
  });
}
...

Did empty localStorage, cache and did a rebuild npm run dev`. So there seems to be a disconnect between webpack chunks for published site.js and page.js based on Page.vue.

Wonder if the wepback chunking in published.js that is turned into site.js is dependent on page.js and on an update of page.vue (turned into page.js) that connection is severed. We did update Page.vue once using CSS width: initial; and that seemed to cause immediate mayhem.

Thoughts how to remedy this?

0 likes
5 replies
LaryAI's avatar
Level 58

It seems like the issue you're experiencing is related to chunk files not being found after deployment. This is a common problem when deploying single-page applications (SPAs) that use code splitting and webpack's chunking. Here's a step-by-step guide to help you troubleshoot and fix the issue:

  1. Versioning: Ensure that your chunk filenames include a hash to bust the cache when a new version is deployed. It looks like you're already doing this with id=[contenthash] in your webpack.mix.js configuration.

  2. Cache Busting: After deploying new changes, browsers might still have the old chunk files cached. To prevent this, you can:

    • Use unique names for each build (which you're doing with [contenthash]).
    • Configure your server to set proper cache-control headers for your chunk files to ensure they are not cached indefinitely.
  3. Service Worker: If you're using a service worker, make sure it's updated to handle the new chunk files. You might need to adjust your service worker cache strategy to ensure it's not serving stale assets.

  4. CleanWebpackPlugin: The CleanWebpackPlugin is configured to clean the js/components/* directory. Ensure that it's not removing chunks that are still needed by the application. You might want to adjust the cleanOnceBeforeBuildPatterns to be more specific or disable it temporarily to see if it's causing the issue.

  5. Consistent Public Path: Ensure that the publicPath is set correctly and consistently in your webpack configuration. It should point to the base URL where your assets are served from.

  6. Check Network Requests: Use the browser's developer tools to check the network requests when the error occurs. Make sure that the requested chunk files actually exist on the server and are accessible.

  7. Error Handling: Implement error handling for failed chunk loads in your application. This can be done by catching errors in your dynamic import() statements and providing a fallback mechanism, such as retrying the import or displaying a user-friendly error message.

  8. Manual Verification: After deployment, manually verify that all the expected chunk files are present on the server and have the correct names corresponding to the latest build.

  9. Build and Deployment Process: Review your build and deployment process to ensure that all the necessary files are being uploaded to the server, and old files are being replaced or removed as needed.

  10. Fallback to Older Chunks: As a temporary measure, you could keep older chunks on the server for a short period after deployment to prevent errors for users who might have the old application code loaded. This is not a long-term solution but can help mitigate immediate issues.

Here's an example of how you might catch and handle a chunk loading error in your Vue component:

const SmtPublishedModule = () => import(/* webpackChunkName: "publish/smt-published-module" */ '../publish/PublishedModule.vue')
  .catch(error => {
    if (/Loading chunk [\d]+ failed/.test(error.message)) {
      window.location.reload(true);
    } else {
      // Handle other errors or report them
      console.error(error);
    }
  });

export default {
  components: {
    SmtPublishedModule,
    // ... other components
  },
};

In this example, if a chunk fails to load, the page will be reloaded, which could potentially resolve the issue if the new chunks are available. However, use this approach with caution as it could lead to a reload loop if not handled properly.

If none of these steps resolve the issue, you may need to provide more details or consult with someone who can access your deployment environment to further diagnose the problem.

rhand's avatar
Level 6

Option 10:

Fallback to Older Chunks: As a temporary measure, you could keep older chunks on the server for a short period after deployment to prevent errors for users who might have the old application code loaded. This is not a long-term solution but can help mitigate immediate issues.

Is a way to deal with it using something like

new CopyWebpackPlugin({
        patterns: [
          {
            from: 'public/js/components',
            to: 'old-chunks',
            globOptions: {
              ignore: ['*.js'],
            },
          },
        ],
      }),

and then possibility add check in published.js for it if date of published file is older than page.js . Either that or we remove chunking for Page.vue data. Not sure yet what to pick. Additional checks for older chunks seem painful, no chunking for Page.vue data may slow things down some. And finally using a manifest or runtime chunk https://webpack.js.org/configuration/optimization/#optimizationruntimechunk . But never tried so not sure if ids are then always added to runtime.js instead of each file.

What I am pretty certain of is this. if site.js is based on published.js then once you publish a site it loads with chunk ids from last deployment/build so when a new deployment is added and all cleaned.. things break .

Love to hear others on this.

rhand's avatar
Level 6

Discussed with main developer on team and he suggested to build on server, but even when we do the with PHP Deployer deploy.yml using

...
tasks:
  deploy:
    - deploy:prepare
    - deploy:vendors
    - artisan:storage:link
    - artisan:view:cache
    - artisan:config:cache
    - artisan:migrate
    - npm:install
    - npm:run:prod
    - deploy:publish
    - artisan:horizon:terminate
  npm:run:prod:
    - run: 'cd {{release_or_current_path}} && npm run prod'...

we still have the webpack chunks id disconnect with published site site.js and app's rebuild page.js.

rhand's avatar
Level 6

When I do add

...
optimization: {
        runtimeChunk: 'single',
      },
...

below output I do get runtime.js added to public folder with

/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			id: moduleId,
/******/ 			loaded: false,
/******/ 			exports: {}
/******/ 		};
...
/******/ 		// object to store loaded and loading chunks
/******/ 		// undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ 		// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
/******/ 		var installedChunks = {
/******/ 			"runtime": 0,
/******/ 			"css/editor": 0,
/******/ 			"css/published": 0,
/******/ 			"css/preview": 0,
/******/ 			"css/login": 0,
/******/ 			"css/dashboard": 0,
/******/ 			"assets/vendor/MediaManager/style": 0
...
/******/ 	(() => {
/******/ 		var chunkToChildrenMap = {
/******/ 			"publish/page": [
/******/ 				"publish/xxx-mini-cookies",
/******/ 				"publish/xxx-published-module",
/******/ 				"publish/xxx-font-class-generator",
/******/ 				"editor/xxx-font-loader"
/******/ 			]
/******/ 		};
/******/ 		__webpack_require__.f.prefetch = (chunkId, promises) => (Promise.all(promises).then(() => {
/******/ 			var chunks = chunkToChildrenMap[chunkId];
/******/ 			Array.isArray(chunks) && chunks.map(__webpack_require__.E);
/******/ 		}));
/******/ 	})();
/******/ 	
/************************************************************************/
/******/ 	
/******/ 	
/******/ })()
;

but that still may not or should not remove the chunk ids being added to files . Do not see it at least.

rhand's avatar
rhand
OP
Best Answer
Level 6

In app/Http/Controllers/Dashboard/PublishController.php we use

Storage::disk('public_published')->put($this->project->subdomain_url.'/js/site.js', file_get_contents(public_path('js/published.js')));

to copy over published.js to site.js for every site. In resources/views/layouts/published-site-template.blade.php that JavaScript is loaded using

...
<link rel="preload" href="{!! $assetDir . 'js/site.js?ver=' . date('YmdHis') !!}" as="script" crossorigin>
...
<script crossorigin type="text/javascript" src="{!! $assetDir . 'js/site.js?ver=' . date('YmdHis') !!}"></script>
...

if we use /js/published.js for path all is well again and site loads. If we publish without this hack all loads well again too as latest published.js is copied over on build. But then I miss version addition to do decent cache busing. I could then add ?ver=' . date('YmdHis') But what is the use of cache busting if I am supposed to get the latest published.js version. Would that not not work? I would need cache busting in the source published.js and that is not really possible.

So perhaps I should keep what we have and enforce a horizon job to renew all published sites and so including copying latest published.js to site.js for each site in the case of a real new app build and do that only at the quiet hours of the day.

However, that would be an issue if existing version for customer is not ready. This as we currently have to draft vs latest published site. So therefore the job should only overwrite site.js with the latest.

Laravel job to enqueue

Storage::disk('public_published')->put($this->project->subdomain_url.'/js/site.js', file_get_contents(public_path('js/published.js')));

for 1 site or all customer websites so we can update site.js post deployment if page.js got updated and new chunk ids added.

Please or to participate in this conversation.