What happens
On a hybrid Pages-Router + App-Router Next 16 app built with Turbopack (the Next 16 default), next build emits all SSR chunks correctly, but OpenNext's bundling does not copy the chunks required by the App Router's framework-default _not-found/page.js and _global-error/page.js into the server function. At runtime every unmatched route (404) and any thrown render error does require(<missing chunk>) → MODULE_NOT_FOUND → HTTP 500, instead of rendering the 404 / error page.
This is distinct from #1028 (a build failure). Here the build succeeds, the deploy succeeds, and only the App Router error routes fail, at runtime.
Minimal reproduction
https://github.com/poysama/opennext-turbopack-appdir-chunk-repro
npm install
npm run build # next build (Turbopack)
npm run opennext # npx --yes @opennextjs/aws@3.9.14 build
npm run check # exits non-zero, lists the dropped chunks
npm run check reports (on a clean clone):
.next ssr chunks: 25
.open-next ssr chunks: 7
error-page chunks referenced: 17, DROPPED from bundle: 15
The triggering app shape is a hybrid app: a pages/ route plus an app/ dir containing only a root layout.tsx and no app/*/page.tsx (the default App Router stub). App Router is active (appType: "hybrid") but has no routes of its own, so the framework-default _not-found is the live 404 handler.
Root cause (as far as I traced it)
copyTracedFiles walks the Pages Router route traces and the shared runtime, but does not walk the App Router page traces (app/**/*.nft.json). Any chunk reachable only through app/_not-found/page.js.nft.json or app/_global-error/page.js.nft.json is therefore never copied into .open-next. The error-page .nft.json traces themselves are correct — they list every required chunk; they just aren't consumed. (Verified: each dropped chunk is referenced only by those traces; the one error-page chunk that survives, [turbopack]_runtime.js, is also referenced by Pages Router traces, which is why it rides along.)
On a larger app the proportions are the same — e.g. one production build: .next had 608 SSR chunks, .open-next 529, and 17 of the 18 chunks app/_not-found/page.js referenced were dropped.
Expected vs actual
- Expected: chunks referenced by App Router page traces are copied into the bundle; 404s render the not-found page.
- Actual: those chunks are dropped; 404s and render errors return HTTP 500.
Why it isn't always visible
Apps that build with --webpack (its output doesn't depend on the dropped chunks/ssr/* files), or that have an App Router catch-all (app/[[...slug]]) so _not-found is never the live handler, won't observe the 500 even though the chunks are still dropped. It surfaces when the App Router framework-default error pages are the live handlers — i.e. hybrid apps with no App Router routes.
Environment
next: 16.2.6 (also reproduced on 16.3.0-canary.32)
@opennextjs/aws: 3.9.14 (reproduced identically on 4.0.3)
node: 22 / 24
adapter target: AWS Lambda (via SST sst.aws.Nextjs)
Workaround
A post-open-next build step that walks app/**/*.nft.json and copies any referenced chunks/ssr/* file present in .next/server but missing from the bundle, into each server function's mirrored .next/server/chunks/ssr/. Happy to share it, or to open a PR adding App Router page traces to copyTracedFiles if that direction is welcome.
What happens
On a hybrid Pages-Router + App-Router Next 16 app built with Turbopack (the Next 16 default),
next buildemits all SSR chunks correctly, but OpenNext's bundling does not copy the chunks required by the App Router's framework-default_not-found/page.jsand_global-error/page.jsinto the server function. At runtime every unmatched route (404) and any thrown render error doesrequire(<missing chunk>)→MODULE_NOT_FOUND→ HTTP 500, instead of rendering the 404 / error page.This is distinct from #1028 (a build failure). Here the build succeeds, the deploy succeeds, and only the App Router error routes fail, at runtime.
Minimal reproduction
https://github.com/poysama/opennext-turbopack-appdir-chunk-repro
npm run checkreports (on a clean clone):The triggering app shape is a hybrid app: a
pages/route plus anapp/dir containing only a rootlayout.tsxand noapp/*/page.tsx(the default App Router stub). App Router is active (appType: "hybrid") but has no routes of its own, so the framework-default_not-foundis the live 404 handler.Root cause (as far as I traced it)
copyTracedFileswalks the Pages Router route traces and the shared runtime, but does not walk the App Router page traces (app/**/*.nft.json). Any chunk reachable only throughapp/_not-found/page.js.nft.jsonorapp/_global-error/page.js.nft.jsonis therefore never copied into.open-next. The error-page.nft.jsontraces themselves are correct — they list every required chunk; they just aren't consumed. (Verified: each dropped chunk is referenced only by those traces; the one error-page chunk that survives,[turbopack]_runtime.js, is also referenced by Pages Router traces, which is why it rides along.)On a larger app the proportions are the same — e.g. one production build:
.nexthad 608 SSR chunks,.open-next529, and 17 of the 18 chunksapp/_not-found/page.jsreferenced were dropped.Expected vs actual
Why it isn't always visible
Apps that build with
--webpack(its output doesn't depend on the droppedchunks/ssr/*files), or that have an App Router catch-all (app/[[...slug]]) so_not-foundis never the live handler, won't observe the 500 even though the chunks are still dropped. It surfaces when the App Router framework-default error pages are the live handlers — i.e. hybrid apps with no App Router routes.Environment
Workaround
A post-
open-next buildstep that walksapp/**/*.nft.jsonand copies any referencedchunks/ssr/*file present in.next/serverbut missing from the bundle, into each server function's mirrored.next/server/chunks/ssr/. Happy to share it, or to open a PR adding App Router page traces tocopyTracedFilesif that direction is welcome.