AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Client Navigation Breaks After ISR Regenerates A Fully Static Redirect Route Into A PPR Route

After an ISR revalidation turns a route that was prerendered as a fully static redirect() into a partially prerendered page, client-side navigations fail: RSC requests receive the static shell only (or 404), never resume, causing React error 412 and a full page reload. The build-time prefetchDataRoute pins the route to serve prebuilt static RSC content, ignoring the new postponed cache entry.

highConfidence 92%Next.jsAffected V16.3.2Affected V16.3.4Affected V16.4.0-Canary.26

Origin Analysis

In next/dist/build/templates/app-page-runtime.js (source packages/next/src/build/templates/app-page.ts), isDynamicRSCRequest is computed from prerenderManifest.routes[pathname].prefetchDataRoute, which is only set for paths that were fully static at build time. When an ISR regeneration later produces a PPR entry with postponed state, isDynamicRSCRequest remains false, so RSC requests are served from cachedData.rscData (or 404 if undefined) instead of resuming the postponed render.
1. Create a Next.js app with cacheComponents: true and a dynamic route /item/[slug] using generateStaticParams returning ['a','b']. Make page call redirect('/') if item missing, otherwise render a <Suspense> child awaiting connection(). Ensure item a is missing at build time. 2. Run next build; observe /item/a is fully static redirect with prefetchDataRoute '/item/a.rsc'. 3. Start next start, then add item a to data source and call revalidateTag('item-a', { expire: 0 }) via an API route or webhook. 4. Request /item/a as a client navigation using headers RSC: 1, Next-Router-State-Tree: ..., Next-Url: /. Or open the home page and click Item A. 5. Observe 200 with x-nextjs-postponed: 1 but body contains only static shell; React error 412 occurs in browser (or 404 on 16.3.x with default file-system cache).

Fixing Code Block

Edge Case Audit

This change only activates when cachedData.postponed is present, so truly static routes are unaffected. However, it relies on cachedData being correctly populated for the requested path; if incrementalCache.get returns null due to cache miss or eviction, isDynamicRSCRequest will remain false and the original bug may still occur. Ensure your cache handler returns postponed state for PPR entries. Rollback: revert this single condition to restore previous behavior. Concurrency: if multiple revalidations race, the cachedData.postponed flag may be transient; use atomic cache updates to avoid serving a mixed entry. Upgrading to a version that includes an official fix is recommended.

Ecosystem Topology