DEV Community

Cover image for Fix ChunkLoadError: Loading Chunk Failed in Next.js
Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on • Originally published at iloveblogs.blog

Fix ChunkLoadError: Loading Chunk Failed in Next.js

You shipped a deploy. Minutes later your error tracker lights up:

ChunkLoadError: Loading chunk 5760 failed.
(error: https://yourapp.com/_next/static/chunks/5760-40c839657ea31a15.js)
Enter fullscreen mode Exit fullscreen mode

Or the timeout variant:

ChunkLoadError: Loading chunk app/layout failed.
(timeout: https://yourapp.com/_next/static/chunks/app/layout.js)
Enter fullscreen mode Exit fullscreen mode

The instinct is to wrap something in a try/catch. Don't — this is almost never a code bug. It is version skew: a browser is asking for a JavaScript chunk that your latest deploy already deleted. This guide explains the real causes and the official Next.js configuration that fixes each one.

{ name: "Next.js", version: "13.4–16" },
{ name: "App Router", version: "& Pages Router" },
{ name: "webpack", version: "chunk loading" },
]} />

What the two variants actually mean

The parenthetical in the message tells you which failure mode you hit:

  • (error: <url>) — the request for the chunk completed but failed, typically a 404. The file no longer exists at that hashed path.
  • (timeout: <url>) — the request never completed within webpack's chunk-load timeout (default 120000 ms, set via output.chunkLoadTimeout). This is the network/slow-connection branch.

Knowing which one you have points you at the right fix below.

Root cause #1: stale chunks after a new deploy (the common one)

Next.js fingerprints static assets with a content hash in the filename (5760-40c839657ea31a15.js). When you deploy, the bundle changes, the hashes change, and the old files are gone. Any client still running the previous version — an open tab, a bfcache restore, a CDN serving stale HTML — requests the old chunk path and gets a 404.

Next.js documents this exact failure mode under self-hosting and calls it version skew: "Missing assets: The client requests JavaScript or CSS files that no longer exist on the server." Rolling deployments make it worse, because some instances serve new assets while others still serve old ones.

Give every build a stable deployment identifier. Next.js appends it (?dpl=…) to asset URLs and exposes it via response headers; when the client detects a mismatch between its deployment ID and the server's, it triggers a hard navigation (full page reload) instead of a broken client-side navigation.

// next.config.js
module.exports = {
  deploymentId: process.env.DEPLOYMENT_VERSION || process.env.GIT_SHA,
}
Enter fullscreen mode Exit fullscreen mode

This is the official, intended mechanism for exactly this cause. See the deploymentId config and self-hosting version skew docs.


deploymentId shipped as experimental.deploymentId in v13.4.10 and was promoted to a top-level config option in v14.1.4. Before 14.1.4, use the experimental. form.

Root cause #2: rolling deploys with mismatched build IDs

If you run multiple containers/instances behind a load balancer, each one generates its own build ID by default, so they disagree on chunk names during a rollout. Pin the build ID so every instance agrees:

// next.config.js
module.exports = {
  generateBuildId: async () => process.env.GIT_HASH,
}
Enter fullscreen mode Exit fullscreen mode

Documented under generateBuildId.

Root cause #3: self-hosted output: 'standalone' builds

If you self-host with output: 'standalone' (common in Docker), the minimal server does not copy public or .next/static by default. Every chunk then 404s until you copy them. Straight from the output docs:

cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/
Enter fullscreen mode Exit fullscreen mode

Root cause #4: a CDN or proxy serving stale HTML

Next.js sets Cache-Control: public, max-age=31536000, immutable on hashed static assets — they are safe to cache forever because the hash changes when the content does. The problem appears when a CDN, reverse proxy, or service worker caches the HTML (which references the hashed chunks) past its life, or rewrites/misroutes /_next/static/ requests.

wrong="CDN caches the HTML document for hours, so it keeps pointing browsers at chunk hashes from a deploy you already replaced."
right="Let the immutable headers on /_next/static stand, keep HTML short-lived, and never strip or redirect /_next/static/ at the proxy. Use assetPrefix if assets live on a separate domain."
/>

If you serve assets from a separate domain/CDN, configure assetPrefix so chunk URLs are generated correctly.

Root cause #5: network failure / timeout (the (timeout:) variant)

A flaky connection, an aggressive ad-blocker, or a slow CDN can make the chunk request hang past webpack's limit. That surfaces as the (timeout:) form. The relevant knob is webpack's output.chunkLoadTimeout (default 120000 ms), reachable through a webpack override in next.config. Treat this as a webpack-level setting, not a Next.js flag.

A safety net for dynamic imports

When you load a component with next/dynamic or React.lazy, React's docs are explicit: "If the Promise rejects, React will throw the rejection reason for the nearest Error Boundary to handle." So a failed chunk load is catchable by an error boundary — you can render a "please refresh" fallback.


The error-boundary catch is documented (see React lazy). The widely-shared "catch ChunkLoadError → reload once via sessionStorage guard" pattern is a community technique, not official guidance. It is a reasonable last-resort UX layer, but it does not address the root cause — ship deploymentId first.

  • The chunk genuinely fails to parse in an old browser (a real SyntaxError, not version skew) — that is a code/transpile-target issue, not a deploy issue.
  • You only see it locally in dev — clear .next and restart; dev chunking differs from production.
  • Your import() path is actually wrong or the module throws at module scope — fix the import, an error boundary only masks it.

Decision guide

Symptom Most likely cause First fix
Spikes right after each deploy Version skew (#1) deploymentId
Only on multi-instance/rolling deploys Mismatched build IDs (#2) generateBuildId
Self-hosted Docker, 404 on every chunk Standalone missing assets (#3) copy .next/static + public
Behind Cloudflare/proxy, intermittent Stale HTML cache (#4) fix CDN/HTML cache headers
(timeout:) in the message Network/slow load (#5) check connectivity / chunkLoadTimeout

Related Articles

Frequently Asked Questions

Why do I get ChunkLoadError only after deploying?

Because a user had a tab open (or a CDN served cached HTML) referencing the old hashed chunk filenames. Your new deploy replaced those files with new hashes, so the old chunk URL now 404s. Next.js calls this version skew — set deploymentId so Next forces a full reload on mismatch.

Is ChunkLoadError a bug in my code?

Usually not. It is a deployment/caching class of problem, which is why upgrading Next.js rarely fixes it. The fix is configuration — deploymentId, generateBuildId, correct CDN headers.

Does a try/catch around import() fix it?

A React error boundary catches the rejected dynamic import so you can show fallback UI. The "reload once" pattern on top is a community technique, not official — use it as a safety net, not the fix.


Originally published at https://www.iloveblogs.blog

Top comments (0)