nextjs - ✅(Solved) Fix PPR metadata resume mismatch despite htmlLimitedBots fully disabling streaming metadata [1 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

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#93401Fetched 2026-05-01 05:32:23
View on GitHub
Comments
1
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
labeled ×2commented ×1

Error Message

Error: Expected the resume to render <div> in this slot but instead it rendered <next_metadata_boundary>. The tree doesn't match so React will fallback to client rendering. digest: '2491006542'

PR fix notes

PR #90259: Always render metadata with Suspense regardless of streaming mode

Description (problem / solution / changelog)

Metadata rendering previously changed its component tree structure based on serveStreamingMetadata — wrapping in Suspense for streaming responses but rendering synchronously for bots. This is unnecessary because bot responses already block on allReady before sending any bytes, so the Suspense boundaries resolve before the response starts regardless. Removing this conditional eliminates serveStreamingMetadata from the metadata closure.

Changed files

  • packages/next/src/lib/metadata/metadata.tsx (modified, +0/-18)
  • packages/next/src/server/app-render/app-render.tsx (modified, +0/-7)

Code Example

npm install
npm run build
npm run start

---

npm run repro:check

---

curl -sS \
  -A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7) AppleWebKit/537.36 Chrome/124 Safari/537.36' \
  -o /tmp/browser.html \
  http://127.0.0.1:3099/posts/alpha

curl -sS \
  -A 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Safari/537.36' \
  -o /tmp/googlebot.html \
  http://127.0.0.1:3099/posts/alpha

---

const nextConfig = {
  cacheComponents: true,
  htmlLimitedBots: /.*/,
}

---

└ ◐ /posts/[slug]
/posts/[slug]
/posts/alpha

---

Error: Expected the resume to render <div> in this slot but instead it rendered <__next_metadata_boundary__>. The tree doesn't match so React will fallback to client rendering.
digest: '2491006542'

---

const serveStreamingMetadata =
  botType && isRoutePPREnabled
    ? false
    : !userAgent
      ? true
      : shouldServeStreamingMetadata(userAgent, nextConfig.htmlLimitedBots)

---

Next.js: 16.2.3
React: 19.1.2
React DOM: 19.1.2
Node.js tested locally: 23.3.0
Runtime: next build + next start
App Router: yes
Cache Components: enabled
PPR: enabled through cacheComponents
htmlLimitedBots: /.*/
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/mdotk/next-ppr-metadata-repro

To Reproduce

Clone the repo and run:

npm install
npm run build
npm run start

In another terminal:

npm run repro:check

Or manually request the PPR route with different user agents:

curl -sS \
  -A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7) AppleWebKit/537.36 Chrome/124 Safari/537.36' \
  -o /tmp/browser.html \
  http://127.0.0.1:3099/posts/alpha

curl -sS \
  -A 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Safari/537.36' \
  -o /tmp/googlebot.html \
  http://127.0.0.1:3099/posts/alpha

The app uses:

const nextConfig = {
  cacheComponents: true,
  htmlLimitedBots: /.*/,
}

The route is a PPR route in the production build:

└ ◐ /posts/[slug]
  ├ /posts/[slug]
  └ /posts/alpha

Current vs. Expected behavior

Current behavior:

  • htmlLimitedBots: /.*/ should fully disable streaming metadata per the docs.
  • Browser-like UA returns 200, but the initial head does not contain the route metadata and the response contains the hidden streaming metadata wrapper.
  • Googlebot/Bingbot return 200 with route metadata in the initial head.
  • Empty/no-UA requests also stream metadata outside the initial head.
  • The server logs a React resume mismatch:
Error: Expected the resume to render <div> in this slot but instead it rendered <__next_metadata_boundary__>. The tree doesn't match so React will fallback to client rendering.
digest: '2491006542'

Expected behavior:

With htmlLimitedBots: /.*/, the prerender/no-UA path and runtime paths should use a consistent metadata component tree shape, or otherwise avoid a PPR resume mismatch. A documented "fully disable streaming metadata" config should not still allow the PPR shell to be generated with a streaming metadata wrapper.

What appears to be happening

In Next 16.2.3, shouldServeStreamingMetadata(userAgent, ".*") returns false for a non-empty browser UA, Googlebot, and Bingbot. But the app-page template still has a no-UA branch that forces streaming metadata:

const serveStreamingMetadata =
  botType && isRoutePPREnabled
    ? false
    : !userAgent
      ? true
      : shouldServeStreamingMetadata(userAgent, nextConfig.htmlLimitedBots)

That means the PPR prerender/export shell can be built with the streaming metadata tree shape, while runtime requests with non-empty UAs can render the blocking metadata tree shape. The metadata component tree changes from a hidden <div> wrapper to direct <__next_metadata_boundary__>, which matches the resume mismatch.

This is in the same problem area as:

https://github.com/vercel/next.js/issues/92087

And this closed PR appears directly relevant:

https://github.com/vercel/next.js/pull/90259

Provide environment information

Next.js: 16.2.3
React: 19.1.2
React DOM: 19.1.2
Node.js tested locally: 23.3.0
Runtime: next build + next start
App Router: yes
Cache Components: enabled
PPR: enabled through cacheComponents
htmlLimitedBots: /.*/

Which area(s) are affected? (Select all that apply)

App Router, Metadata, Partial Prerendering, Runtime

Which stage(s) are affected? (Select all that apply)

next build, next start

Additional context

The minimal reproduction digest is 2491006542. The important signal is the metadata tree-shape mismatch: expected <div>, got <__next_metadata_boundary__>.

extent analysis

TL;DR

The most likely fix is to update the Next.js configuration to ensure consistent metadata component tree shapes for prerender and runtime requests.

Guidance

  • Review the htmlLimitedBots configuration to ensure it is correctly set to disable streaming metadata for all user agents.
  • Verify that the shouldServeStreamingMetadata function is correctly implemented to handle different user agents and bot types.
  • Check the app-page template to ensure it does not force streaming metadata for non-empty user agents.
  • Consider updating to a newer version of Next.js, as the issue may be related to a known bug that has been fixed in a later version.

Example

No code snippet is provided, as the issue is related to a specific configuration and implementation.

Notes

The issue appears to be related to a known bug in Next.js 16.2.3, and updating to a newer version may resolve the issue. However, without more information, it is difficult to provide a definitive solution.

Recommendation

Apply a workaround by updating the htmlLimitedBots configuration and verifying that the shouldServeStreamingMetadata function is correctly implemented, as the issue may be related to a known bug that has been fixed in a later version.

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