Docs: should clarify whether we can call redirect within generateMetadata
@icyJoseph is already working on this.
Since Jun 25, 2025.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.4k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
What is the documentation issue?
It is unclear whether redirect, permanentRedirect, and notFound can be called from generateMetadata.
- permanentRedirect: “permanentRedirect can be used in Server Components, Client Components, Route Handlers, and Server Actions”. No mention of
generateMetadataso it sounds like it is prohibited. - redirect: “redirect can be used in Server Components, Route Handlers, and Server Actions.” No mention of
generateMetadataso it sounds like it is prohibited. Also does not include Client Components although there is an example of client component below. - notFound just says to use it “within a route segment”; does not say specifically where.
- generateMetadata says “The redirect() and notFound() Next.js methods can also be used inside generateMetadata.”, contradicting the
redirectdocumentation. Also no mention ofpermanentRedirect.
I tested a basic page.tsx:
import { redirect } from "next/navigation";
import type { Metadata } from 'next';
export async function generateMetadata(): Promise<Metadata> {
await new Promise(resolve => setTimeout(resolve, 1000))
redirect('/generate-metadata-redirect');
}
export default async function Page() {
await new Promise(resolve => setTimeout(resolve, 2000))
redirect('/server-component-redirect');
}
In nextjs 14, it seems that the first redirect wins, and the redirect from generateMetadata is used. On the other hand, in nextjs 15, it seems that the Server Component redirect always wins.
On the other hand, if the Server Component does not throw a redirect:
import { redirect } from "next/navigation";
import type { Metadata } from 'next';
export async function generateMetadata(): Promise<Metadata> {
await new Promise(resolve => setTimeout(resolve, 1000))
redirect('/generate-metadata-redirect');
}
export default async function Page() {
await new Promise(resolve => setTimeout(resolve, 2000))
return (
<div>
<h1>My Path Page</h1>
</div>
);
}
Then nextjs 14 returns the 307 redirect from generateMetadata, but nextjs 15 returns status code 200 but then performs a redirect using javascript. The source code of the page contains the stack trace:
<template data-dgst="NEXT_REDIRECT;replace;/generate-metadata-redirect;307;" data-msg="Switched to client rendering because the server rendering errored:
NEXT_REDIRECT" data-stck="Switched to client rendering because the server rendering errored:
Error: NEXT_REDIRECT
at Module.generateMetadata (webpack-internal:///(rsc)/./app/mypath/page.tsx:13:62)
at resolveErrorDev (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:44590)
at getOutlinedModel (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:32934)
at /Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:36809
at ResponseInstance._fromJSON (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:37621)
at JSON.parse (<anonymous>)
at initializeModelChunk (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:25635)
at resolveModelChunk (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:25007)
at /Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:57533
at /Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:57638
at progress (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:186:57690)" data-cstck="
at MetadataOutlet (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/metadata/async-metadata.js:24:11)
at Suspense (<anonymous>)
at AsyncMetadataOutlet (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/metadata/async-metadata.js:38:11)
at StreamingMetadataOutlet [Server] (<anonymous>)
at __next_outlet_boundary__ (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/metadata/metadata-boundary.js:36:15)
at InnerLayoutRouter (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:243:11)
at RedirectErrorBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/redirect-boundary.js:74:9)
at RedirectBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/redirect-boundary.js:82:11)
at HTTPAccessFallbackBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/http-access-fallback/error-boundary.js:96:11)
at LoadingBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:324:11)
at ErrorBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/error-boundary.js:175:11)
at InnerScrollAndFocusHandler (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:140:9)
at ScrollAndFocusHandler (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:225:11)
at RenderFromTemplateContext (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/render-from-template-context.js:16:44)
at OuterLayoutRouter (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:360:11)
at InnerLayoutRouter (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:243:11)
at RedirectErrorBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/redirect-boundary.js:74:9)
at RedirectBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/redirect-boundary.js:82:11)
at HTTPAccessFallbackErrorBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/http-access-fallback/error-boundary.js:88:9)
at HTTPAccessFallbackBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/http-access-fallback/error-boundary.js:96:11)
at LoadingBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:324:11)
at ErrorBoundary (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/error-boundary.js:175:11)
at InnerScrollAndFocusHandler (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:140:9)
at ScrollAndFocusHandler (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:225:11)
at RenderFromTemplateContext (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/render-from-template-context.js:16:44)
at OuterLayoutRouter (webpack-internal:///(ssr)/./node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/client/components/layout-router.js:360:11)
at body (<anonymous>)
at html (<anonymous>)
at RootLayout [Server] (<anonymous>)
at RedirectErrorBoundary (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:248:4631)
at RedirectBoundary (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:248:4727)
at AppDevOverlayErrorBoundary (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:274:11584)
at AppDevOverlay (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:274:215362)
at HotReload (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:274:219791)
at Router (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:248:7783)
at ErrorBoundaryHandler (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:254:2994)
at ErrorBoundary (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:254:4291)
at AppRouter (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:248:14186)
at ServerInsertedHTMLProvider (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:418:461)
at ServerInsertedMetadataProvider (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:422:2243)
at App (/Users/yonran/Documents/tutorials/nextjs-redirects/nextjs-dashboard/node_modules/.pnpm/next@15.3.4_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:422:12743)"></template><!--/$-->
I think the documentation should be clarified to say whether redirect, permanentRedirect, and notFound can or should be called from generateMetadata.
Is there any context that might help us understand?
No context.
Does the docs page already exist? Please link to it.
https://nextjs.org/docs/app/api-reference/functions/redirect
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.
Assessment
This issue has not been assessed yet.