Link to the code that reproduces this issue
https://github.com/maxesse/next-redirect-marker-repro
To Reproduce
- Clone the reproduction and run it with
next build && next start.
- Visit the redirect-capable route. It renders a component behind Suspense which calls
redirect(), causing Next to add #__next-page-redirect to the streamed document.
- Navigate away using
<Link>. With Cache Components enabled, the previous route is retained in a hidden React Activity.
- Confirm that
document.getElementById('__next-page-redirect') still finds the marker in the hidden route.
- Click another unrelated same-origin
<Link>.
The final click performs a full document navigation instead of a normal App Router navigation.
The reproduction includes a small window sentinel and network checks to make the document navigation easy to see. Without the stale marker, the same Link click is a normal SPA navigation.
I reproduced this on Next 16.3.3 and 16.4.0-canary.8.
Current vs. Expected behavior
Current behavior
A redirect marker belonging to an inactive route retained in a hidden Activity affects later navigation from the active route.
The cause appears to be the document-wide check in navigate-reducer:
if (document.getElementById('__next-page-redirect')) {
return completeHardNavigation(state, url, navigateType)
}
Because the lookup searches the whole document, it also finds a redirect marker inside an inactive Activity subtree and turns an unrelated same-origin Link click into a full-page navigation.
I originally found this while using @next/playwright's instant() helper. In that case the unexpected MPA navigation lands on the PPR shell, which makes the failure initially look like cached content was not prefetched.
Expected behavior
A redirect marker belonging to an inactive hidden route should not affect navigation from the currently active route.
Once a streamed redirect is no longer relevant, its marker should either be removed/consumed, or the navigation check should only consider a redirect belonging to the active route.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 25.6.0: Fri Jul 31 19:17:26 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T6041
Available memory (MB): 36864
Available CPU cores: 14
Binaries:
Node: 24.16.0
npm: 12.0.2
Yarn: 1.22.22
pnpm: 11.21.0
Relevant Packages:
next: 16.4.0-canary.9
eslint-config-next: N/A
react: 19.2.8
react-dom: 19.2.8
typescript: 5.9.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
cacheComponents, Linking and Navigating, Redirects, Loading UI and Streaming
Which stage(s) are affected? (Select all that apply)
next build (local), next start (local)
Additional context
This looks related to #92767 and the Next team regression test in #92615, but I believe it is a separate case.
Those concern stale RedirectBoundary/route state being restored from Activity. This reproduction is specifically about the streamed __next-page-redirect DOM marker remaining in an inactive route and later being found by the document-global check in navigate-reducer.
It is also adjacent to the broader hidden Activity behavior tracked in #86577.
The real application where I found this uses a request-time authentication/access check behind Suspense, which may call redirect('/sign-in'). This is why a streamed redirect can occur during an otherwise valid cached navigation, but the minimal reproduction does not use an auth library.
Link to the code that reproduces this issue
https://github.com/maxesse/next-redirect-marker-repro
To Reproduce
next build && next start.redirect(), causing Next to add#__next-page-redirectto the streamed document.<Link>. With Cache Components enabled, the previous route is retained in a hidden React Activity.document.getElementById('__next-page-redirect')still finds the marker in the hidden route.<Link>.The final click performs a full document navigation instead of a normal App Router navigation.
The reproduction includes a small
windowsentinel and network checks to make the document navigation easy to see. Without the stale marker, the same Link click is a normal SPA navigation.I reproduced this on Next 16.3.3 and 16.4.0-canary.8.
Current vs. Expected behavior
Current behavior
A redirect marker belonging to an inactive route retained in a hidden Activity affects later navigation from the active route.
The cause appears to be the document-wide check in
navigate-reducer:Because the lookup searches the whole document, it also finds a redirect marker inside an inactive Activity subtree and turns an unrelated same-origin Link click into a full-page navigation.
I originally found this while using
@next/playwright'sinstant()helper. In that case the unexpected MPA navigation lands on the PPR shell, which makes the failure initially look like cached content was not prefetched.Expected behavior
A redirect marker belonging to an inactive hidden route should not affect navigation from the currently active route.
Once a streamed redirect is no longer relevant, its marker should either be removed/consumed, or the navigation check should only consider a redirect belonging to the active route.
Provide environment information
Operating System: Platform: darwin Arch: arm64 Version: Darwin Kernel Version 25.6.0: Fri Jul 31 19:17:26 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T6041 Available memory (MB): 36864 Available CPU cores: 14 Binaries: Node: 24.16.0 npm: 12.0.2 Yarn: 1.22.22 pnpm: 11.21.0 Relevant Packages: next: 16.4.0-canary.9 eslint-config-next: N/A react: 19.2.8 react-dom: 19.2.8 typescript: 5.9.3 Next.js Config: output: N/AWhich area(s) are affected? (Select all that apply)
cacheComponents, Linking and Navigating, Redirects, Loading UI and Streaming
Which stage(s) are affected? (Select all that apply)
next build (local), next start (local)
Additional context
This looks related to #92767 and the Next team regression test in #92615, but I believe it is a separate case.
Those concern stale RedirectBoundary/route state being restored from Activity. This reproduction is specifically about the streamed
__next-page-redirectDOM marker remaining in an inactive route and later being found by the document-global check innavigate-reducer.It is also adjacent to the broader hidden Activity behavior tracked in #86577.
The real application where I found this uses a request-time authentication/access check behind Suspense, which may call
redirect('/sign-in'). This is why a streamed redirect can occur during an otherwise valid cached navigation, but the minimal reproduction does not use an auth library.