If you upgraded to Next.js 16 and your production build now dies with something like:
Error occurred prerendering page "/_not-found"
you're not looking at a config typo. You're looking at one of three distinct regressions that all surface through the same internal route, because /_not-found is the one page every App Router build always tries to prerender — even if you never touch it. That makes it the canary for a handful of unrelated Next.js 16 problems.
This is the diagnostic path that actually separates the three causes, based on the confirmed GitHub reports (vercel/next.js#85604, #86978, #84994) and what's changed since Next.js 15.
Why /_not-found breaks even though you never wrote code for it
The App Router auto-generates a /_not-found route from your root not-found.js/layout.js tree. Next.js prerenders it as a static fallback during next build, which means any import anywhere in your root layout — even three levels deep in a dependency — gets pulled into that prerender pass. In Next.js 15 this was forgiving. In Next.js 16, prerendering got stricter about what's allowed to run without a request context, and about what a Server Component is allowed to pass to a Client Component. /_not-found is where that strictness first bites, because it has to render with zero real request.
Cause 1 — a function is being passed to a Client Component
The most common variant, matching #85604: your build log says
Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server"
This happens when something in your root layout — often a <Link> wrapper, an analytics provider, or a custom error boundary — passes a callback prop (an onClick, a formatter function, a render-prop) from a Server Component into a Client Component that also gets rendered on the /_not-found path. Next.js 15 silently serialized this in some cases; Next.js 16 rejects it outright.
Fix: find the component tree your root layout.js renders unconditionally (headers, providers, global nav) and audit every prop passed into a 'use client' boundary. Any function prop needs to either move inside the Client Component itself, or be wrapped as a Server Action with 'use server' if it genuinely needs to run server-side.
Cause 2 — workUnitAsyncStorage invariant under Turbopack
If your error instead reads:
Error [InvariantError]: Invariant: Expected workUnitAsyncStorage to have a store
(seen in #86978 on Next.js 16.0.8 + Turbopack), the cause is a dependency calling headers(), cookies(), or draftMode() unconditionally at module scope or during prerendering, with no request context available. One confirmed repro traced this to react-instantsearch-nextjs@1.x calling headers() on the server with no guard. Because /_not-found prerenders with no real request, any library that assumes a request always exists will throw here first.
Fix: audit third-party packages that touch Next.js server APIs (search widgets, i18n libraries, analytics SDKs) for calls to headers()/cookies() that aren't wrapped in try/catch or gated behind connection(). Pin the dependency to a version predating the offending change, or move the call behind a component that only renders on genuinely dynamic routes — never in the root layout that /_not-found inherits.
Cause 3 — pageExtensions or custom route config touching the internal route
A smaller number of reports (#65447 and older) trace back to a custom pageExtensions array in next.config.mjs that inadvertently matches internal Next.js files, causing the framework's own _not-found implementation to be shadowed or double-resolved. If your error trace mentions pageExtensions or a resolution conflict rather than a runtime crash, check that config first — it's the cheapest cause to rule out.
How to tell which one is yours in under 5 minutes
- Run
next buildwith--debugif available, or read the full stack trace above the "Error occurred prerendering page /_not-found" line — the real error is always printed just before it. - "Functions cannot be passed directly to Client Components" → Cause 1.
- "workUnitAsyncStorage" or "InvariantError" → Cause 2.
- Anything mentioning
pageExtensionsor route resolution → Cause 3. - If none of those match, bisect: temporarily strip your root
layout.jsdown to a bare<html><body>{children}</body></html>and rebuild. If the build passes, reintroduce providers one at a time until it breaks again.
Prevention going forward
Treat your root layout as prerender-critical code, not just app chrome. Every provider or wrapper you add there runs during the /_not-found prerender whether you like it or not. If you're also migrating cache behavior at the same time, read Next.js 16 "use cache" errors: why your Cache Components break — the two upgrades often get tackled in the same PR and the failure modes can look similar in the build log.
Related reading
- Next.js build: Module not found — the real causes
- Next.js "Dynamic server usage" couldn't be rendered statically — fix
- Next.js build passes locally, breaks in production — a postmortem
- Next.js App Router: the complete guide
- Next.js 16: next lint removed, ESLint flat config only
Originally published at https://www.iloveblogs.blog
Top comments (0)