Skip to content

Turbopack: dynamic import() failures stay failed no recovery path without a full page reload #93489

@kumarajay0412

Description

@kumarajay0412

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

  1. Run:

    pnpm next build --turbo && pnpm next start

    or:

    pnpm next dev --turbo
  2. Open the app, but do not click the button yet.

  3. In DevTools → Network, switch to Offline.

  4. Click the button.

    The dynamic import rejects with:

    Failed to load chunk ...

    which is expected.

  5. Switch DevTools back to Online.

  6. 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:

Failed to load chunk ...

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:

    next build --turbo
  • Dev command:

    next dev --turbo

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Lazy LoadingRelated to Next.js Lazy Loading (e.g., next/dynamic or React.lazy).TurbopackRelated to Turbopack with Next.js.

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions