-
Notifications
You must be signed in to change notification settings - Fork 30.7k
Description
Link to the code that reproduces this issue
To Reproduce
git clone https://github.com/franky47/next-16.2.0-canary.0-repro
cd next-16.2.0-canary.0-repro
pnpm install
pnpm build # Fails with Turbopack (default)
pnpm build --webpack # WorksA dynamic route app/blog/[slug] has an opengraph-image.tsx that imports from app/data.ts, which uses top-level await (making it an async module). Any module that transitively depends on async modules (content collections, MDX processing, etc.) will trigger this.
Current vs. Expected behavior
Current: Build fails during static page generation with:
Error occurred prerendering page "/blog/some-slug"
TypeError: Cannot read properties of undefined (reading 'default')
at collectStaticImagesFiles (resolve-metadata.ts)
Expected: Build succeeds, as it does with next build --webpack or on Next.js 16.1.7.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.6.0: Mon Jan 19 22:01:58 PST 2026; root:xnu-11417.140.69.708.3~1/RELEASE_ARM64_T6041
Available memory (MB): 131072
Available CPU cores: 16
Binaries:
Node: 24.11.0
npm: 11.6.2
Yarn: 1.22.22
pnpm: 10.27.0
Relevant Packages:
next: 16.2.1-canary.1 // Latest available version is detected (16.2.1-canary.1).
eslint-config-next: N/A
react: 19.2.4
react-dom: 19.2.4
typescript: 5.9.3
Next.js Config:
output: N/AWhich area(s) are affected? (Select all that apply)
Turbopack
Which stage(s) are affected? (Select all that apply)
next build (local)
Additional context
I detected this issue when upgrading to [email protected] in the nuqs docs app.
Root cause
Bisected to PR #88487 ("Turbopack: retain loader tree order for metadata"), which shipped in 16.2.0-canary.0.
That PR changed how Turbopack references dynamic metadata image modules in the app-page template (crates/next-core/src/app_page_loader_tree.rs):
Before (16.1.7, working):
import __TURBOPACK__openGraph__5__ from "METADATA_5";
// ...
metadata: { openGraph: [__TURBOPACK__openGraph__5__] }After (16.2.0, broken):
const __TURBOPACK__openGraph__5__ = require("METADATA_5");
// ...
metadata: { openGraph: [__TURBOPACK__openGraph__5__.default] }The require() is synchronous, but the metadata module can be async when opengraph-image.tsx transitively depends on async modules (top-level await, async module initialization from libraries like fumadocs-mdx, content collections, etc.). The synchronous require() returns before async initialization completes, so .default is undefined. The route tree is then constructed with [undefined] in the metadata arrays.
Later, collectStaticImagesFiles in resolve-metadata.ts iterates the array and calls interopDefault(undefined) → undefined.default → TypeError.
Why webpack is unaffected: The webpack code path in metadata/discover.ts wraps dynamic metadata images in async factory functions:
(async (props) => (await import(/* webpackMode: "eager" */ "...")).default(props))The .default access is deferred until the async import resolves.
Why static images are unaffected in Turbopack: write_static_metadata_item in the same Rust file already wraps static images in async factory functions. Only write_metadata_item for MetadataWithAltItem::Dynamic uses the broken synchronous pattern.
Suggested fix (from Opus 4.6)
Wrap dynamic metadata items in async factory functions (matching both the webpack behavior and the existing Turbopack write_static_metadata_item behavior), so .default is accessed after async initialization completes:
// Instead of:
const mod = require("METADATA_5");
metadata: { openGraph: [mod.default] }
// Wrap in an async factory:
metadata: { openGraph: [async (props) => {
const mod = await import("METADATA_5");
return mod.default(props);
}] }Bisect results
| Version | Result |
|---|---|
| 16.1.7 | pass |
| 16.2.0-canary.0 | fail |
| 16.2.0 | fail |
Additional context
- PR Turbopack: retain loader tree order for metadata #88487 was fixing #87322 (infinite compilation loop caused by ESM import hoisting breaking loader tree order). The fix is correct for that issue, but the switch to synchronous
require()+ immediate.defaultdoesn't account for async metadata modules. - Any project using content layer tools (fumadocs-mdx, Contentlayer, etc.) that compile MDX at build time will likely hit this, since the compiled MDX modules are async under Turbopack.