vercel / vercel/next.js

Incorrect HTTP error status codes from non-streaming generateMetadata

Open
#82,041 10 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Error Handling
Dominant language
JavaScript
Stars
142k
Forks
32.4k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/p00000001/error-status-codes-bug

To Reproduce
  1. Start the application
  2. Click the Throw notFound() page link and monitor the http status response in DevTools
  3. Click the Throw redirect() page link and monitor the http status response in DevTools
Current vs. Expected behavior
  • In Next.js 15, generateMetadata() became async to support metadata streaming by default: https://nextjs.org/docs/app/api-reference/functions/generate-metadata#streaming-metadata
  • When streaming, I understand it is not possible to change the HTTP response code as the response headers have already been sent. Therefore, by default calling notFound() and redirect() from generateMetadata() will return a 200 response code and use client-side JavaScript to handle functionality.
  • next.config.js permits a property htmlLimitedBots to block streaming until generateMetadata() is resolved.
  • When streaming is blocked for a user agent, calling notFound() or redirect() from generateMetadata() should return a 404 and 307 status code respectively as streaming has not started.
  • This works as expected when there is no layout.js in the root directory. However, when there is a layout.js file present, the status code is always 200 - the same behaviour as when streaming is active.
  • When generateMetadata() is blocking streaming, I would have expected the same status code behaviour regardless of the presence of layout.js, as the desired status code is already known before streaming has started?
  • I appreciate some crawlers can make “soft” interpretations of embedded not found page content and redirect meta tags, but this is not as reliable as http status codes, and even for crawlers that can, it could use up extra “crawl budget” processing the page.
  • To test this, I have created a sample app, and changed the config to: htmlLimitedBots: /.+/ so that all user agents get blocked generateMetadata().
Provide environment information
Occurs on live Vercel
Which area(s) are affected? (Select all that apply)

Error Handling

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

Vercel (Deployed), next start (local), next dev (local)

Additional context

Live example on Vercel: https://error-status-codes-bug.vercel.app/

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked reproduction app and its next.config.js, using htmlLimitedBots: /.+/ to compare behavior with and without a root layout.js. Reproduce notFound() and redirect() from generateMetadata() in DevTools across local and deployed modes; done means the blocked cases return 404 and 307 regardless of root layout presence.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nextjs
Domain
backend, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.