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

PPR Dynamic Segment Serves Fallback Shell After RevalidateTag(..., { Expire: 0 })

After calling revalidateTag(tag, { expire: 0 }) for a tag used by a 'use cache' function that feeds the static shell of a prerendered dynamic segment (enumerated by generateStaticParams and wrapped in root Suspense), Next.js 16.3.x permanently serves the route's root Suspense fallback shell instead of the full shell. The static HTML file remains intact on disk but is never served again; revalidatePath does not repair it; only a rebuild restores the full shell. Profile forms ('max', 'seconds') do not exhibit this bug.

highConfidence 85%Next.jsAffected V16.3.0Affected V16.3.1Affected V16.3.1-Canary.21

Origin Analysis

When revalidateTag with expire:0 is used on a tag consumed by a PPR dynamic-segment shell, Next.js invalidates the cached route payload/shell entry but does not trigger a synchronous re-prerender of the segment. As a result, the dynamic segment is no longer considered statically generated at serve time, causing the route to fall back to its root Suspense boundary and stream all content instead of using the intact .html shell. This violates the expected PPR revalidation contract where shell and postponed state should regenerate together.
1. Create a PPR-enabled dynamic route /[slug] with generateStaticParams and a root Suspense, reading a 'use cache' function tagged 'products'. 2. Build and start the app. 3. Request /a and confirm the static shell contains content. 4. Call revalidateTag('products', { expire: 0 }) in a route handler. 5. Request /a again: observe that the response is the fallback shell (content only in streamed segments), and this persists indefinitely. 6. Attempt revalidatePath('/a') – still fallback shell. 7. Rebuild – shell restored.

Fixing Code Block

Edge Case Audit

Using expire:1 introduces a brief stale window (up to 1 second) where visitors may see outdated content. If strict freshness is required, this workaround is insufficient. Also, if multiple tags or nested cache layers are involved, all must use the same positive TTL to avoid inconsistent fallback behavior. Rollback: if the workaround causes unacceptable staleness, revert to expire:0 and manually trigger a rebuild after each purge until the underlying Next.js bug is fixed. Monitor Next.js releases for an official fix.

Ecosystem Topology