`next dev` reported missing Suspense boundaries in layouts that the
build accepted. For `/[top]/items/[bottom]`, `generateStaticParams`
returned `[{ top: 't1' }]`, and the page wrapped its `bottom` access in
Suspense. A request for `/t2/items/b2` still reported the layout's
access to `top` as an error. Development treated both parameters as
unresolved because the requested `top` value was not generated, although
the required static shell only needed to defer `bottom`.
Production Cached Navigations used the same overly broad parameter set
and omitted eligible static content from repeat visits. Resumes also
reconstructed that set from the original build manifest, even after an
on-demand prerender had produced a more complete shell.
This replaces the alternative proposed in #98460. That proposal fixes
the development error by selecting a separate fallback parameter set for
validation while keeping response staging unchanged. The two sets are
not intended to differ for the same shell target. Correcting only
validation would preserve the incorrect staging decision and leave
production Cached Navigations without the eligible static content.
Staging and static-shell validation now use one `stagedFallbackParams`
set for each selected shell target. Required partial shells retain their
unresolved parameters, even when a later request can complete them.
Prerenders record their parameter set in postponed state, and resumes
use that recorded set rather than reconstructing it from the generic
source.
Dynamic RSC requests now read and revalidate the completed-shell cache
key, so they find partial artifacts that the fully resolved pathname
lookup missed. Request metadata and `RequestStore` both expose the set
as `stagedFallbackParams`. Action-only fallback detection checks actual
unresolved parameters instead of treating deferred values as missing.
### Why?
Should come up with better solution that does not block PRs with git
conflict
x-ref:
https://vercel.slack.com/archives/C02CDC2ALJH/p1785263902728189?thread_ts=1785263687.502649&cid=C02CDC2ALJH
### How?
- Delete `errors.json`, the error-code SWC plugin, generated WASM, merge
driver, and validation/build tooling.
- Stop attaching error codes to server-rendering digests, redboxes, and
telemetry; native `Error.code` and `Error.name` remain available where
applicable.
- Remove the development-overlay error feedback UI, middleware, and
telemetry event that depended on stable codes.
- Update fixtures, snapshots, and guidance for code-free errors and
numeric-only digests.
<!-- NEXT_JS_LLM -->
## Summary
The instant / blocking-prerender insight console errors printed a docs
URL under each fix option (two or three anchors per message). This
collapses them to a single `Learn more:` link at the end of each
message, while keeping the `[stream]`/`[cache]`/`[block]` fix-option
labels. It restores the single-link format these builders originally
shipped with.
**Before:**
```
Ways to fix this:
- [stream] Provide a placeholder with `<Suspense fallback={...}>` around the data access
https://nextjs.org/docs/messages/blocking-prerender-runtime#wrap-in-or-move-into-suspense
- [block] Set `export const instant = false` to allow a blocking route
https://nextjs.org/docs/messages/blocking-prerender-runtime#allow-blocking-route
```
**After:**
```
Ways to fix this:
- [stream] Provide a placeholder with `<Suspense fallback={...}>` around the data access
- [block] Set `export const instant = false` to allow a blocking route
Learn more: https://nextjs.org/docs/messages/blocking-prerender-runtime
```
Covers all 16 insight-kind errors, across `blocking-route-messages.ts`,
`sync-io-messages.ts`, `dynamic-rendering-utils.ts`, and
`instant-messages.ts`.
Because the dev overlay classified these errors by the `#`-anchored docs
URL, `getBlockingRouteErrorDetails` is updated to match the anchor-less
`Learn more:` URL. Console-message snapshots and the guidance-data
extraction test are updated to the new format. The dev-overlay fix-card
data (`instant-guidance-data.ts`) keeps its per-card links, since those
are the overlay UI rather than the console message.
## Verification
- `pnpm --filter=next types`
- Snapshots regenerated with `jest -u` for the affected suites
<!-- NEXT_JS_LLM -->
We forked test assertions in places in the early days of merging
Turbopack. Now that we more closely align, a bunch of these had the
exact same assertions. Fold them into one.
## What
Removes the `silence this warning` phrasing from the structured fix
lines in instant validation output:
- `- [block] Set \`export const instant = false\` to ~~silence this
warning and~~ allow a blocking route`
- `- [ignore] Set \`export const instant = false\` to ~~silence this
warning and~~ opt the route out of instant-navigation validation`
- `- [ignore] Set \`export const instant = false\` to ~~silence this
warning and~~ opt the dropped segment out of instant-navigation
validation`
## Why
The line called itself a warning while being logged via `Error:`. This
PR originally renamed it to `silence this error`, but that has the
mirror problem: at validation level `warning` or `manual-warning` (or
when the check only surfaces in dev) nothing blocks, so "error"
over-claims.
The verb is also wrong either way. Setting `instant = false` doesn't
silence a problem that still exists. It declares that blocking is
acceptable for the route, so validation stops treating it as one.
Removing the clause leaves wording that is correct at every validation
level and matches the dev overlay cards ("Allow blocking route",
"Disable validation on this route") and the docs sections the lines link
to.
## How
- `blocking-route-messages.ts`, `dynamic-rendering-utils.ts`: drop
`silence this warning and` from the `[block]` lines
- `instant-messages.ts`: drop it from the `[ignore]` lines (unrendered
segment, link prefetch)
- `errors.json`: regenerated, append-only (codes 1394-1405)
- Storybook fixture and 15 test files updated to the new strings
<!-- NEXT_JS_LLM_PR -->
When Cache Components is enabled, the development server threads a
`fallbackParams` request meta for dynamic app routes so the staged
render knows which params are not statically known and must be deferred
to a later stage. The previous computation walked the prerendered routes
from `getStaticPaths` and kept the one with the fewest fallback params,
without checking that the route actually matched the requested URL.
Consider `/mixed/[lang]/[id]` where `generateStaticParams` covers `lang:
'en'` but not `id`: the prerendered routes are the base
`/mixed/[lang]/[id]`, which defers `[lang, id]`, and the covered
`/mixed/en/[id]`, which defers only `[id]`. For the request
`/mixed/fr/123` the fewest-fallback route is `/mixed/en/[id]`, but `en`
does not match `fr`, so applying its `[id]` set left `lang` out of the
fallback set and `fr` was treated as a statically known value.
Because this meta decides which stage each param resolves in, and the
stage decides the environment a replayed `console.log` is attributed to,
treating `fr` as static resolved it in the prerender stage instead of
deferring it to the runtime stage. The computation now matches the
requested URL against each prerendered route with the canonical
`getRouteRegex` and, among the routes that match, picks the
most-specific one, the one with the fewest fallback params. For
`/mixed/fr/123` only the base route matches, so its `[lang, id]` set is
used and both params defer, while for `/mixed/en/123` the covered
`/mixed/en/[id]` still matches and wins, so `lang` resolves statically
and only `id` defers. This mirrors what a production build writes to the
prerender manifest, where the server matches the URL to the
most-specific prerendered route at request time. The change is
development-only, gated on the route module being in dev mode, and
production continues to read the manifest. A later change in this stack
reads the same `fallbackParams` meta for the Instant Navigation testing
API's on-demand shell render, so that path defers the identical per-URL
set a production prefetch would.
### What?
Adds "Copy prompt" button to all 33 instant-guidance fix cards. Updates
card links, factory `Learn more:` URLs, and overlay routing to the new
docs slugs. Adds `[group]` tag prefix to CLI fix bullets so agents can
map them back to card prompts.
### Why?
Cards tell developers _what_ to do. The button gives agents a
ready-to-paste instruction. The `[group]` tag lets agents reading CLI
output find the matching card in the docs without parsing prose.
### How?
- `prompt` field on all 33 `FixCard` entries.
- Button replaces the external-link icon in the top-right; link moves
next to the label.
- Card links updated to `blocking-prerender-*` and
`instant-unrendered-segment` slugs (avoids overriding upstream pages).
- Variant-aware URL routing for `metadata` and `viewport` (matches
existing `blocking-route` pattern). `InstantHeaderExplanation` takes a
`variant` prop.
- Fix bullets prefixed with their card group: `[cache]`, `[stream]`,
`[block]`, etc. Tags match `<FixOption group>` in the MDX docs.
- CLI bullets use `unstable_instant = false` (the current API). Overlay
cards keep `instant` (aspirational).
- Metadata dynamic-marker bullet now mentions the Suspense wrapper.
- Merged canary: unrendered-segment errors land in the Insights tab via
`isInstantNavigationError`.
### Depends on
- [vercel/front#71640](https://github.com/vercel/front/pull/71640) — 6
sync-IO pages
- [vercel/front#71781](https://github.com/vercel/front/pull/71781) — 4
metadata/viewport pages + `instant-unrendered-segment`
### What?
Adds a tab bar to the dev overlay that separates normal errors
("Errors") from instant navigation errors ("Insights"). The indicator
pill also reflects the split.
### Why?
When `unstable_instant` validation produces navigation-phase errors
alongside regular prerender errors, they were mixed into a single list.
Developers had no way to tell which errors were structural
instant-validation issues versus regular runtime/prerender errors.
### Demo
- [Demo 1: prerender
blocking-route](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/01-cookies-body)
— `Blocking Route` badge (red), Errors tab.
- [Demo 2: navigation
blocking-route](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/42-subnav-fetch)
— `Instant` badge (amber), Insights tab.
### How?
- `Errors` splits `runtimeErrors` into `normalErrors` / `instantErrors`
(using the existing `inNavigation` flag from
`getBlockingRouteErrorDetails`) and defaults to whichever bucket has
errors.
- `ErrorTabBar` renders between the nav and dialog inside
`ErrorOverlayLayout` (new `tabBar` prop). Empty tabs are disabled.
- `ErrorOverlay` passes a `key` derived from the error composition so
tab state resets when the shape changes (e.g. normal errors resolve).
- `RenderErrorContext` gains `instantErrorCount`; the indicator pill
shows "N Issues", "N Insights", or "N Issues · N Insights" accordingly.
- Prerender errors show `Blocking Route` badge (red), navigation errors
show `Instant` badge (amber).
---------
Co-authored-by: Yavor Punchev <yavor.punchev@gmail.com>
### What?
- Rename cards to plain English: `Prerender params if known`, `Mark the
route as dynamic`, `For telemetry, use a timing API`.
- Remove `Wrap body in Suspense` card from viewport variants.
- Body errors: `during the initial render` → `during prerendering`;
`blocking navigation` → `blocking the page load`.
- Server sync IO leads with `the unstable value <expression>`.
- Client sync IO drops `fixed at build time`.
- New loading-state icon for the `block` group.
### Demo
- [Fix
Overview](https://error-messages-overhaul-ibsl.labs.vercel.dev/fix-overview)
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
### What?
Redesigns the blocking-route error overlay for instant navigation errors
with a distinct "Instant" overlay path, visual technique cards, and
updated error wording framed around navigation impact.
### Why?
The current overlay dumps every possible cause and fix in one block of
text. The new design is friendlier — amber "Instant" badge, a short
headline framed around navigation, and responsive code snippet cards
showing each fix pattern.
### Demo
- **Runtime template** (e.g. `cookies()`):
https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/26-cookies-ssr-no-instant
- **Dynamic template** (e.g. uncached `fetch`):
https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/27-fetch-ssr-no-instant
### How?
**Overlay**
- New early-return path in `errors.tsx` for `blocking-route` errors
without refinement — renders `InstantRuntimeError` with CodeFrame →
description → technique cards → CallStack → ErrorCause
- `InstantGuidance` component with responsive CSS grid of fix technique
cards (3 per variant)
- Color-coded cards with colored borders and matching highlight text
(blue, purple, red)
- "Make route params static" card (runtime only) has a dashed border
indicating it's conditional
**Build & CLI messages**
- Build output messages extracted into `blocking-route-messages.ts` and
deduplicated across `dynamic-rendering.ts`
- `dynamicOrRuntimeBodyMessage` added for build-time static validation
where the specific cause can't be pinpointed — lists all APIs
(`fetch(...)`, `cookies()`, `headers()`, `params`, `searchParams`,
`connection()`)
- `isRuntimeVariant()` replaces the old `includes('cookies()')`
heuristic which broke because both templates mention `cookies()`
- `logBuildDebugHint()` extracted and shared between
`logDisallowedDynamicError` and instant validation — adds "run `next
dev`" and "`next build --debug-prerender`" hints to instant validation
build output
Results:
<img width="2094" height="1478" alt="Google Chrome 2026-04-17 16 37 00"
src="https://github.com/user-attachments/assets/04f126c5-250c-4e6e-bad0-d6960496cd13"
/>
<img width="1978" height="1512" alt="Google Chrome 2026-04-17 16 36 37"
src="https://github.com/user-attachments/assets/46a502d8-9500-4055-814b-3efb739949db"
/>
---------
Co-authored-by: Janka Uryga <lolzatu2@gmail.com>
### What?
Adds `params` and `searchParams` to the Dynamic variant of the
blocking-route error message.
### Why?
When `const { id } = await params` in a Page component triggers the
blocking-route error, the message says "Uncached data or `connection()`
was accessed outside of `<Suspense>`" — which doesn't mention `params`
at all. This makes it hard for developers and AI agents to connect the
error to `await params` being the cause, or to find the "Params and
SearchParams" fix section in the linked error doc.
### How?
Updated two error message strings in `dynamic-rendering.ts` (and the
corresponding `errors.json` entry) to include `params` and
`searchParams` alongside "Uncached data" and `connection()`.
## PR checklist (Fixing a bug)
- Errors have a helpful link attached:
https://nextjs.org/docs/messages/blocking-route
Made with [Cursor](https://cursor.com)
By sending the dynamic validation errors to the browser via WebSocket,
instead of rendering a validation outlet into the dynamic dev render
stream, we can avoid artificially delaying the spawned validation to
implicitly wait for the different chunks (static, runtime, and dynamic),
without blocking the dev render stream, and instead wait explicitly for
all chunks to accumulate.
Previously, we were using React's console replaying to get full fidelity
error stacks in the browser (including inspectable virtual server
modules). Now, we're using the same underlying mechanism by sending a
separate RSC stream through the WebSocket that contains only the errors.
In the browser, the received errors are then logged with
`console.error`, which also triggers that they're displayed in a
collapsed Redbox, as was the case before with the replaying.
---------
Co-authored-by: Janka Uryga <lolzatu2@gmail.com>
Prior to this change any "hole" in a prerender that would block the
shell was considered an error and you would be presented with a very
generic message explaining all the different ways you could have failed
this validation check.
With this change we use a new technique to validate the static shell
which can now tell the difference between waiting on uncached data or
runtime data. It also improves the heuristics around generateMetadata
and generateViewport errors.
Added new error pages for runtime sync IO and ensure we only validate
sync IO after runtime data if the page will be validating runtime
prefetches.
Restored the validation on HMR update so you can get feedback after
saving a new file.
---
We've also discovered that hanging inputs are not handled correctly.
Fixing this is non-trivial and will be done in a follow-up, so for now,
we're disabling the failing tests.
---------
Co-authored-by: Josh Story <story@hey.com>
Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
This moves `experimental.cacheComponents` to a top level config. As part
of this, I disabled some tests in `build-output-prerender` that assert
on `cacheComponents` appearing in the experimental list. In a separate
PR, I'm going to show that Cache Components is enabled next to the
bundler info.
This also updates some docs pages to remove "experimental" language.
Previously we used the immediate queue to schedule the consecutive tasks
that would prerender and abort and page prerender. This works fine but
since React does not consider immediates as IO for async work tracking
it means we can't use it for scheduling more advanced cases like
static->runtime->dynamic where the IO that unblocks in runtime phase can
be picked up.
While React could change to include immediates as IO the argument here
is that it isn't really IO since it is immediate work while timeouts
generally have to schedule something to fire later with timeout zero
being a sort of special case where later === now.
So instead we are going to move to scheduling our consecutive tasks
using timeout as well that way we can align the Next.js and React
heuristic for identifying IO and make certain kinds of debugging easier
to implement.
When validating a dynamic route in dev with Cache Components we must not
use more params than would be know at build time because we want to
ensure dev acts as an appropriate debug environment for build validation
issues. Any time a validation fails for reasons other than fallback
params it would have failed as well with fallback params. So we find the
smallest set of fallback params that would be used during the build
(which might be empty if you provide a complete set through
generateStaticParams) and we use that.