Skip to content
loke.dev
A glowing modular pathway curves around a cracked dark block on a navy background

Next.js 16 “Module Factory Is Not Available”: Diagnose Turbopack HMR

Identify the Cache Components and lazy-loading Turbopack HMR regression, isolate it with Webpack, and validate production separately.

Published 7 min read

The short answer

If Next.js 16 throws “Module … was instantiated … but the module factory is not available” after you edit a Server Component, first treat it as a development bundler problem—not proof that your data, cache, or production runtime is broken. The clearest known trigger combines Turbopack Hot Module Replacement, Cache Components or a file-level use cache directive, a lazy-loaded client boundary, and a dynamic route. The open Next.js issue reproduces that combination and scopes the affected stage to next dev.

The safest immediate mitigation is to run development with Webpack while leaving the production build unchanged until you have evidence that production is affected:

{
  "scripts": {
    "dev": "next dev",
    "dev:webpack": "next dev --webpack",
    "build": "next build"
  }
}

Next.js documents --webpack as the supported way to use Webpack instead of the default Turbopack bundler in development. This is a supported diagnostic switch, not an undocumented environment variable. See the Next.js CLI reference.

Confirm that you have the same failure

Do not diagnose from the last sentence of the stack trace alone. The same wording can appear when different module graphs fail. The reported Cache Components regression has a more specific fingerprint:

  • The error appears in next dev, commonly after saving a Server Component or repeating Fast Refresh.
  • The stack mentions app-rsc, a client reference proxy, or a lazy-loaded client module.
  • The affected route sits under a layout or component using the use cache directive, with cacheComponents enabled.
  • The tree includes React.lazy or next/dynamic and often a dynamic or catch-all segment.
  • A full reload may recover temporarily, but the next source edit can recreate the failure.

The upstream reproduction says removing use cache, using Webpack, or removing the lazy-loaded component stops the failure. It also reports that replacing React.lazy with next/dynamic does not help. Those observations come from the issue’s minimal reproduction; test them against your own tree rather than assuming every “module factory” error has this cause.

A recent Next.js community report and a separate Payload CMS community report show the same error shape during a Next.js 16 Cache Components migration. These threads establish current developer pain; they are not the source of the technical diagnosis.

Use a two-run test to isolate Turbopack

Run the same edit sequence twice from a cleanly restarted development server:

  1. Start your normal next dev command, open the affected route, and save the same Server Component several times.
  2. Stop that server and run npm run dev:webpack (or the equivalent command for your package manager).
  3. Repeat the same edits and navigation path.

If the error is repeatable under Turbopack but not Webpack, you have isolated the bundler/HMR path without changing application behavior. Keep the alternate script while you investigate. If both bundlers fail on the initial page load, stop using this guide as the diagnosis: inspect the first application error, server log, import boundary, and dependency compatibility instead.

Do not make “delete every cache” your permanent fix. Restarting the server or removing generated output may clear a stale development graph, but recurrence after the next edit is evidence that the trigger remains.

Why this combination is suspicious

Next.js 16 made Turbopack the default bundler and introduced Cache Components as an opt-in caching model. The Next.js 16 announcement describes both changes. With cacheComponents: true, the use cache documentation allows a file, component, or function to become cacheable.

The upstream issue shows that the failure occurs while Turbopack updates the development module graph around a cached layout and a lazy client boundary. That is the supported conclusion. It is reasonable to infer that HMR retained an invalid relationship between module instances and factories, but the issue does not yet identify a merged root-cause fix. Do not present that inference as settled framework internals.

Mitigation 1: use Webpack for development

This is the lowest-risk option when you need to keep both Cache Components and lazy loading. It changes the development bundler, not the route’s caching contract.

{
  "scripts": {
    "dev": "next dev",
    "dev:webpack": "next dev --webpack"
  }
}

Use the Webpack script only for affected work if the rest of the team benefits from Turbopack. Record the exact Next.js version and link the upstream issue in the temporary workaround so that somebody can remove it deliberately later.

Mitigation 2: narrow the cache boundary

A file-level use cache on a layout makes every exported async function in that file part of the cached scope. If the layout also owns a lazy client boundary, move the cache directive closer to the data read when that still matches the product’s freshness rules.

The following is an illustrative refactor. Replace the query and lifetime with values that match your application:

import { cacheLife } from 'next/cache'
import NavigationClient from './NavigationClient'

async function getNavigation() {
  'use cache'
  cacheLife('hours')

  return db.navigation.findMany({
    orderBy: { position: 'asc' },
  })
}

export default async function Layout({
  children,
}: {
  children: React.ReactNode
}) {
  const items = await getNavigation()

  return (
    <>
      <NavigationClient items={items} />
      {children}
    </>
  )
}

This refactor is appropriate only if the navigation result is safe to share and may be stale for the chosen lifetime. The existing loke.dev Cache Components guide explains how to choose sharing, lifetime, and invalidation rules. Do not move user-specific data into a shared cache merely to avoid a development error.

Mitigation 3: remove the lazy boundary temporarily

If the component is small or already needed on the initial route, a static import can be a reasonable temporary trade. The upstream reproduction reports that removing the lazy-loaded component avoids its failure:

// Temporary diagnostic: replace a lazy boundary with a static import.
import DynamicPanel from './DynamicPanel'

export default async function Layout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <>
      <DynamicPanel />
      {children}
    </>
  )
}

Measure the resulting client bundle and loading behavior before keeping this change. A workaround that removes code splitting can shift cost from development reliability to every visitor.

Do not confuse an HMR regression with a production failure

The reported Next.js issue marks the affected stage as local development and reproduces the problem after source edits. That evidence does not establish a production build or runtime failure. Keep the checks separate:

  • Run next build with the same configuration used in deployment.
  • Start the production server or preview artifact and load the affected dynamic routes directly.
  • Navigate between routes that cross the lazy boundary.
  • Exercise cached reads and invalidation separately from module loading.
  • Check browser and server logs from the first request, not only after a development hot update.

If production also fails, capture a minimal reproduction for that path. Do not cite the development-only issue as proof of the production cause.

A compact decision table

Error only after saves; Webpack development is stable: keep your application code, use next dev --webpack, and watch the upstream issue.

Error disappears when a broad use cache is narrowed: keep the narrower boundary only if its sharing and freshness contract is correct.

Error disappears with a static import: decide whether the lost code splitting is acceptable; otherwise prefer the Webpack development script.

Error occurs on the first load under both bundlers: investigate imports, client/server boundaries, and dependency versions; this specific HMR regression is not yet isolated.

Error occurs in a production artifact: create a production reproduction and treat it as a separate incident.

When can you remove the workaround?

As of August 3, 2026, the upstream issue is open and shows no linked pull request or milestone. Before returning to Turbopack development, check that issue and the release notes for your target Next.js version. Then rerun the exact edit sequence that originally failed; an upgrade alone is not a verification.

Leave a short comment beside the alternate script with the issue URL, the affected route, and the test that proves the workaround is no longer needed. That turns a mysterious permanent flag into a reversible engineering decision.

Sources

Primary technical sources: Next.js issue #85538; Next.js CLI reference; Cache Components configuration; use cache directive; and the Next.js 16 release announcement.

Demand evidence only: recent reports in the Next.js and Payload CMS communities.