Incorrect HTTP error status codes from non-streaming generateMetadata
Open
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
- Start the application
- Click the
Throw notFound() pagelink and monitor the http status response in DevTools - Click the
Throw redirect() pagelink 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()andredirect()fromgenerateMetadata()will return a 200 response code and use client-side JavaScript to handle functionality. next.config.jspermits a propertyhtmlLimitedBotsto block streaming untilgenerateMetadata()is resolved.- When streaming is blocked for a user agent, calling
notFound()orredirect()fromgenerateMetadata()should return a 404 and 307 status code respectively as streaming has not started. - This works as expected when there is no
layout.jsin the root directory. However, when there is alayout.jsfile 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 oflayout.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 blockedgenerateMetadata().
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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