nextjs - 💡(How to fix) Fix cacheComponents: true Server Actions on dynamic routes ([param]) return prerendered HTML shell (text/html) instead of RSC payload (text/x-component) in next start, causing "An unexpected response was received from the server" [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#92716Fetched 2026-04-15 06:18:42
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Timeline (top)
closed ×1commented ×1labeled ×1locked ×1

Root Cause

Root cause hypothesis With cacheComponents: true, PPR generates and stores a .html postponed shell for every dynamic route at build time. When next start receives a Server Action POST (identified by the next-action header), it appears to be incorrectly routing it through the PPR shell-serving path instead of the Server Action handler path.

Fix Action

Fix / Workaround

Attempted workarounds that did NOT fix it:

Attempted workaround that DID fix it (unacceptable for production):

What is the fix ? Is there a supported way to opt a specific dynamic route out of PPR shell generation with cacheComponents: true, so Server Actions on those routes work correctly in next start? If not, this appears to be a bug in how next start dispatches POST requests to PPR dynamic routes —the next-action header should take precedence over the PPR shell path.

Code Example

content-type: text/html; charset=utf-8
x-nextjs-postponed: 1
x-nextjs-prerender: 1
x-nextjs-stale-time: 300
RAW_BUFFERClick to expand / collapse

Next.js: 16.2.x React: 19.2.0 Turbopack: enabled (default in 16) cacheComponents: true Deployment: Self-hosted (next start) — NOT Vercel

Describe the bug When cacheComponents: true is enabled and a Server Action is called from a dynamic route segment (e.g. /admin/curriculum/[courseId]), the server returns content-type: text/html with the prerendered PPR shell instead of content-type: text/x-component. This causes the Next.js client to throw: An unexpected response was received from the server.

This only happens when I build the project and run the production build locally.... It does NOT happen in: next dev Deployed to Vercel It works fine in dev mode and on vercel...

To Reproduce

Enable cacheComponents: true in next.config.ts Create a dynamic route: app/admin/[courseId]/page.tsx Place a Client Component on the page that calls a Server Action (e.g. a dialog that submits a form) Run next build && next start Trigger the Server Action Observe the POST request in DevTools response is text/html with x-nextjs-postponed: 1 and x-nextjs-prerender: 1 instead of text/x-component

Investigation findings After next build, the .next/server/app/admin/[courseId].html file is generated a prerendered PPR shell. When next start receives the Server Action POST to /admin/[courseId], instead of routing it to the action handler, it serves this prerendered shell, returning text/html. The response headers on the failing POST:

content-type: text/html; charset=utf-8
x-nextjs-postponed: 1
x-nextjs-prerender: 1
x-nextjs-stale-time: 300

The .meta file for the route contains a full postponed blob baked at build time. Key proof moving to a non-dynamic route fixes it: When the exact same page and Server Action are moved to simple route like this (e.g. app/admin/courses/page.tsx with no [param]), no .html file is generated in .next/server/app/admin/, and Server Actions work correctly. The issue is exclusively triggered by dynamic route segments with cacheComponents: true.

Attempted workarounds that did NOT fix it:

Adding await connection() from next/server at the top of the page the .html shell is still generated at build time and still served for POSTs Adding export const dynamic = "force-dynamic" incompatible with cacheComponents, throws a build warning and has no effect Adding Supabase data fetching (cookies(), headers()) directly in the page shell is still generated Deleting/bypassing the proxy (proxy.ts ) entirely no effect Adding generateStaticParams returning [] no effect

Attempted workaround that DID fix it (unacceptable for production):

Moving the page out of the dynamic route segment into a hardcoded path stopped the .html from being generated and Server Actions started working. This confirms the issue is specifically tied to dynamic route segments + cacheComponents PPR shell generation.

Root cause hypothesis With cacheComponents: true, PPR generates and stores a .html postponed shell for every dynamic route at build time. When next start receives a Server Action POST (identified by the next-action header), it appears to be incorrectly routing it through the PPR shell-serving path instead of the Server Action handler path.

Expected behavior A POST request with a next-action header to a PPR dynamic route should always be handled as a Server Action and return content-type: text/x-component, regardless of whether a prerendered shell exists for that route. Actual behavior The prerendered .html shell is returned with content-type: text/html, breaking the Server Action entirely.

What is the fix ? Is there a supported way to opt a specific dynamic route out of PPR shell generation with cacheComponents: true, so Server Actions on those routes work correctly in next start? If not, this appears to be a bug in how next start dispatches POST requests to PPR dynamic routes —the next-action header should take precedence over the PPR shell path.

extent analysis

TL;DR

The issue can be potentially fixed by opting specific dynamic routes out of PPR shell generation or by modifying the Next.js configuration to prioritize Server Action handling over PPR shell serving.

Guidance

  • Investigate the possibility of excluding specific dynamic routes from PPR shell generation using Next.js configuration options, although the issue does not provide a clear method for doing so.
  • Consider modifying the Server Action handler to handle cases where the PPR shell is served instead of the expected content-type, potentially by checking the response headers and handling the error accordingly.
  • Review the Next.js documentation for any updates or configuration options related to PPR shell generation and Server Action handling, as the issue may be related to a known limitation or bug.
  • If possible, test the application with a newer version of Next.js to see if the issue has been resolved in a later version.

Example

No code example is provided due to the lack of specific technical details in the issue.

Notes

The issue appears to be related to a specific combination of Next.js configuration options and Server Action handling, and may require further investigation or updates to the Next.js framework to resolve.

Recommendation

Apply a workaround by modifying the application to handle cases where the PPR shell is served instead of the expected content-type, as a supported fix is not currently available.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING