vercel / vercel/next.js

Instant-navigation validation reports a deliberate `redirect()` from a layout as a validation failure

Open
#98,711 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Error Overlay Redirects
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/orcunbalcilar/nextjs-instant-validation-redirect-repro

To Reproduce
  1. npm install && npm run dev
  2. Request /dashboard twicecurl -s -o /dev/null http://localhost:3000/dashboard, then the same again.

The second request is the point: on the cold compile the message is suppressed (the devRenderDidError branch in getNavigationDisallowedDynamicReasons), so a single request looks like it does not reproduce.

/dashboard has a layout in the shape the Cache Components migration guide recommends — the request-bound read is kept out of the layout's top level, inside a <Suspense> boundary — and that shell redirects when a cookie is absent:

// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <Suspense fallback={<p>loading shell…</p>}>
      <Shell>{children}</Shell>
    </Suspense>
  )
}

async function Shell({ children }: { children: React.ReactNode }) {
  const jar = await cookies()
  if (!jar.get("seeded")) {
    redirect("/elsewhere") // an ordinary gate, not an error
  }
  return <main>{children}</main>
}
Current vs. Expected behavior

Current. Every warm request to /dashboard logs:

Error: Route "/dashboard": Could not validate `instant` because the target segment was prevented
from rendering, likely due to the following error.
Error: An error occurred while attempting to validate instant UI. This error may be preventing the
validation from completing.
    at DashboardLayout (app/dashboard/layout.tsx:13:5)
  [cause]: Error: NEXT_REDIRECT
      at Shell (app/dashboard/layout.tsx:23:13)
    environmentName: 'Prefetch',
    digest: 'NEXT_REDIRECT;replace;/elsewhere;307;'

Expected. A redirect() is deliberate control flow, not a render failure. I would expect validation to treat the route as "redirected, nothing to validate" and stay silent, the way catchError already declines to swallow the router sentinels.

Why this matters beyond the noise. While the redirect is thrown, validation for that request is inconclusive, so a genuine finding on the same route is not reported. Replace app/dashboard/page.tsx with a Server Component that does blocking IO:

import { connection } from "next/server"

export default async function DashboardPage() {
  await connection()
  return <p>dashboard</p>
}

then request /dashboard twice each way:

Request Logged
curl -H 'Cookie: seeded=1' .../dashboard (redirect does not fire) Next.js encountered uncached data …blocking-prerender-dynamic — correct
curl .../dashboard (redirect fires) only Could not validate \instant` …`; the real finding is gone

So the message can hide the diagnostics it exists to surface.

Neither documented opt-out applies:

Attempt Result
export const instant = false on app/dashboard/layout.tsx no change — the reported line numbers shift by the added line, so the config is loaded, but the message stays
export const instant = false on app/dashboard/page.tsx hard 500 — "instant" is a route segment config and can only be used when the segment is a Server Component module

An interactive dashboard page is normally a Client Component, so the page-level opt-out is unavailable in exactly the case that needs it. #98666 reaches the same dead end from a different cause (next/root-params).

Where it comes from. Both call sites in packages/next/src/server/app-render/app-render.tsx (onError and onBrowserBailout) pass the thrown value straight to trackThrownErrorInNavigation, which wraps whatever it receives when there is no validation boundary on the component stack. Neither filters the router sentinels. isNextRouterError already exists for this, and its own doc comment describes the case precisely: "These errors are thrown by user code to perform navigation operations and interrupt the React render."

Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.6.0: Fri Jul 31 19:18:49 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T6000
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 26.8.2
  npm: 11.19.1
  Yarn: 1.22.22
  pnpm: 12.3.4
Relevant Packages:
  next: 16.4.0-canary.32 // Latest available version is detected (16.4.0-canary.32).
  eslint-config-next: N/A
  react: 19.3.0
  react-dom: 19.3.0
  typescript: 7.0.2
Next.js Config:
  output: N/A
Which area(s) are affected? (Select all that apply)

cacheComponents, Redirects, Error Overlay

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

next dev (local)

Additional context

First hit on 16.3.5; reproduced unchanged on 16.4.0-canary.32. Found in a real app where the workspace layout gates on a session and a saved-project seed, so it fired on every cold entry to the dashboard. The reproduction above was reduced from it.

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

Reproduce the issue with the linked app by requesting /dashboard twice, then inspect both trackThrownErrorInNavigation call sites in packages/next/src/server/app-render/app-render.tsx and the devRenderDidError branch in getNavigationDisallowedDynamicReasons. Compare the thrown value with isNextRouterError, and verify that deliberate redirects no longer produce instant-validation errors while the blocking-prerender-dynamic finding still appears when the cookie is present.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, next.js, react
Domain
backend, testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.