Link to the code that reproduces this issue
https://github.com/kumarajay0412/turbopack-chunk-load-repro
To Reproduce
Turbopack dynamic import() failures persist after transient network errors with no built-in retry
When a dynamic import() rejects (e.g. user briefly offline, transient 5xx, network blip), the failure persists across subsequent import() calls for the same chunk for a meaningful window of time, even after connectivity is fully restored.
The only reliable recovery is a hard page reload.
There is currently no built-in retry mechanism in the Turbopack runtime, and the webpack-retry-chunk-load-plugin ecosystem solution is not applicable because Turbopack does not expose a plugin system.
This makes UI flows that trigger dynamic imports in response to user interaction (lazy-loaded heavy modules behind a button click) brittle on flaky networks.
Reproduction
Minimal Next.js 15 app.
Both next dev --turbo and next build --turbo reproduce the issue.
Project structure
my-app/
app/
page.tsx
components/
heavy-module.ts
app/page.tsx
"use client";
import { useState } from "react";
export default function Page() {
const [status, setStatus] = useState<string>("idle");
const onClick = async () => {
setStatus("loading");
try {
const mod = await import("../components/heavy-module");
mod.run();
setStatus("ok");
} catch (err) {
setStatus(`error: ${(err as Error).message}`);
}
};
return <button onClick={onClick}>Run ({status})</button>;
}
components/heavy-module.ts
export const run = () => console.log("loaded");
Steps to reproduce
-
Run:
pnpm next build --turbo && pnpm next start
or:
-
Open the app, but do not click the button yet.
-
In DevTools → Network, switch to Offline.
-
Click the button.
The dynamic import rejects with:
which is expected.
-
Switch DevTools back to Online.
-
Click the button again multiple times within ~5 seconds.
Expected behavior
Once connectivity is restored, the next click should successfully load the chunk.
Actual behavior
Subsequent clicks continue to fail with the same:
error for several seconds, even though the browser is fully online again.
Eventually one click succeeds, but only after a delay long enough that many users would assume the app is broken and refresh the page.
If a service worker is registered, or the browser places the failed request into HTTP negative cache, the failure can persist even longer.
Representative console output
Failed to load chunk /_next/static/chunks/<hash>._.js
from module [project]/components/heavy-module.ts [app-client]
(ecmascript, async loader)
at async Promise.all
at async Page.onClick
What I tried
1. Inline retry with .catch()
const mod = await import("../components/heavy-module").catch(async () => {
await new Promise((r) => setTimeout(r, 300));
return import("../components/heavy-module");
});
This still hits the same cached failure and rejects again.
2. Longer retry + waiting for online event
This improves things slightly but remains racy and fragile.
The failure cache TTL is not deterministic.
3. Full page reload
window.location.reload();
This reliably fixes the issue, but is disruptive because it destroys in-memory application state.
4. webpackPrefetch
This helps only for predictable navigation flows.
It does not help for interaction-triggered imports, and the prefetch itself can fail silently.
5. Replacing dynamic import with static import
This completely avoids the problem, but increases the initial bundle size.
At the moment, this is the most reliable workaround.
What I'd hope for
1. Built-in retry support in the Turbopack chunk loader
Ideally configurable via next.config.js, for example:
- max retries
- retry delay
- exponential backoff strategy
Equivalent in spirit to webpack-retry-chunk-load-plugin.
There is related discussion in #82651 where retry support was mentioned as considered but not scheduled.
2. Stable runtime hooks
A documented runtime API/hook that lets applications implement retry logic globally rather than wrapping every import() call site manually.
3. Documentation clarification
Explicit documentation stating that:
- dynamic
import() failures are not automatically retried by Turbopack
- failed chunk loads may remain cached temporarily by the runtime/browser
- applications should implement retry handling if lazy-loaded modules are critical
Environment
-
Operating System: macOS / Linux / Windows
-
Node.js: <node -v>
-
Next.js: <package version>
-
Browser: Chrome <version>, Firefox <version>
-
Build command:
-
Dev command:
Additional context
This is a real production concern for apps where important features are hidden behind dynamic imports:
- editors
- feature-flagged experiences
- large SDKs
- AI tooling
- visualization libraries
Webpack users historically had community solutions like:
webpack-retry-chunk-load-plugin
Turbopack users currently do not have an equivalent option.
As a result, teams often end up:
- reverting to webpack via
next build --webpack
- eagerly bundling everything
- wrapping every dynamic import in custom retry logic
- forcing full page reloads after chunk failures
Current vs. Expected behavior
Current behavior
When a dynamically imported chunk fails to load due to a transient network issue (offline state, flaky connection, temporary 5xx, etc.), subsequent import() calls for the same chunk continue to fail for a period of time even after connectivity is restored.
The failed state appears to be cached either by:
- the Turbopack runtime
- the browser's module loader / HTTP negative cache
- or a service worker layer
As a result:
- repeated retries immediately fail
- short retry loops are ineffective
- users often need to hard refresh the page
- interaction-triggered lazy loading becomes unreliable on unstable networks
Expected behavior
Once connectivity is restored, a subsequent import() attempt should transparently retry fetching the chunk and succeed without requiring a page reload.
Ideally, Turbopack should provide one of the following:
- automatic retry behavior for failed chunk loads
- configurable retry/backoff support
- or runtime hooks allowing global retry handling
At minimum, failed dynamic imports should not remain stuck in a cached rejected state for several seconds after the network is healthy again.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin 25.2.0
Available memory (MB): 24576
Available CPU cores: 10
Binaries:
Node: 24.14.1
npm: 11.12.1
pnpm: 10.28.0
Relevant Packages:
next: 15.5.14
react: 18.3.1
react-dom: 18.3.1
typescript: 5.8.3
Next.js Config:
output: N/A
Build command: next build --turbo
Dev command: next dev --turbo
Which area(s) are affected? (Select all that apply)
Lazy Loading
Which stage(s) are affected? (Select all that apply)
Vercel (Deployed)
Additional context
No response
Link to the code that reproduces this issue
https://github.com/kumarajay0412/turbopack-chunk-load-repro
To Reproduce
Turbopack dynamic import() failures persist after transient network errors with no built-in retry
When a dynamic
import()rejects (e.g. user briefly offline, transient 5xx, network blip), the failure persists across subsequentimport()calls for the same chunk for a meaningful window of time, even after connectivity is fully restored.The only reliable recovery is a hard page reload.
There is currently no built-in retry mechanism in the Turbopack runtime, and the
webpack-retry-chunk-load-pluginecosystem solution is not applicable because Turbopack does not expose a plugin system.This makes UI flows that trigger dynamic imports in response to user interaction (lazy-loaded heavy modules behind a button click) brittle on flaky networks.
Reproduction
Minimal Next.js 15 app.
Both
next dev --turboandnext build --turboreproduce the issue.Project structure
my-app/ app/ page.tsx components/ heavy-module.tsapp/page.tsxcomponents/heavy-module.tsSteps to reproduce
Run:
pnpm next build --turbo && pnpm next startor:
Open the app, but do not click the button yet.
In DevTools → Network, switch to Offline.
Click the button.
The dynamic import rejects with:
which is expected.
Switch DevTools back to Online.
Click the button again multiple times within ~5 seconds.
Expected behavior
Once connectivity is restored, the next click should successfully load the chunk.
Actual behavior
Subsequent clicks continue to fail with the same:
error for several seconds, even though the browser is fully online again.
Eventually one click succeeds, but only after a delay long enough that many users would assume the app is broken and refresh the page.
If a service worker is registered, or the browser places the failed request into HTTP negative cache, the failure can persist even longer.
Representative console output
What I tried
1. Inline retry with
.catch()This still hits the same cached failure and rejects again.
2. Longer retry + waiting for
onlineeventThis improves things slightly but remains racy and fragile.
The failure cache TTL is not deterministic.
3. Full page reload
This reliably fixes the issue, but is disruptive because it destroys in-memory application state.
4.
webpackPrefetchThis helps only for predictable navigation flows.
It does not help for interaction-triggered imports, and the prefetch itself can fail silently.
5. Replacing dynamic import with static import
This completely avoids the problem, but increases the initial bundle size.
At the moment, this is the most reliable workaround.
What I'd hope for
1. Built-in retry support in the Turbopack chunk loader
Ideally configurable via
next.config.js, for example:Equivalent in spirit to
webpack-retry-chunk-load-plugin.There is related discussion in #82651 where retry support was mentioned as considered but not scheduled.
2. Stable runtime hooks
A documented runtime API/hook that lets applications implement retry logic globally rather than wrapping every
import()call site manually.3. Documentation clarification
Explicit documentation stating that:
import()failures are not automatically retried by TurbopackEnvironment
Operating System: macOS / Linux / Windows
Node.js:
<node -v>Next.js:
<package version>Browser: Chrome
<version>, Firefox<version>Build command:
Dev command:
Additional context
This is a real production concern for apps where important features are hidden behind dynamic imports:
Webpack users historically had community solutions like:
webpack-retry-chunk-load-pluginTurbopack users currently do not have an equivalent option.
As a result, teams often end up:
next build --webpackCurrent vs. Expected behavior
Current behavior
When a dynamically imported chunk fails to load due to a transient network issue (offline state, flaky connection, temporary 5xx, etc.), subsequent
import()calls for the same chunk continue to fail for a period of time even after connectivity is restored.The failed state appears to be cached either by:
As a result:
Expected behavior
Once connectivity is restored, a subsequent
import()attempt should transparently retry fetching the chunk and succeed without requiring a page reload.Ideally, Turbopack should provide one of the following:
At minimum, failed dynamic imports should not remain stuck in a cached rejected state for several seconds after the network is healthy again.
Provide environment information
Operating System: Platform: darwin Arch: arm64 Version: Darwin 25.2.0 Available memory (MB): 24576 Available CPU cores: 10 Binaries: Node: 24.14.1 npm: 11.12.1 pnpm: 10.28.0 Relevant Packages: next: 15.5.14 react: 18.3.1 react-dom: 18.3.1 typescript: 5.8.3 Next.js Config: output: N/A Build command: next build --turbo Dev command: next dev --turboWhich area(s) are affected? (Select all that apply)
Lazy Loading
Which stage(s) are affected? (Select all that apply)
Vercel (Deployed)
Additional context
No response