"Error: NEXT_REDIRECT" in Next.js: Why redirect() Isn't Working, Tested
redirect() from next/navigation doesn't return. It throws a special error called NEXT_REDIRECT, and Next catches it higher up and turns it into the redirect. If your own try/catch catches it first, the redirect never happens, and the only sign is "NEXT_REDIRECT" in your logs.
I tested redirect() in Server Actions, pages, a Route Handler and a client component on Next.js 16.4.4 with a production build.
The usual cause: redirect() inside try
"use server";
import { redirect } from "next/navigation";
export async function save() {
try {
await db.insert(/* ... */);
redirect("/done"); // throws, and the catch below swallows it
} catch (e) {
console.log("CAUGHT", (e as Error).message, (e as { digest?: string }).digest);
}
}CAUGHT NEXT_REDIRECT NEXT_REDIRECT;push;/done;307;The button press did nothing: the browser stayed on the form page. The digest shows what the error carries: the method, the target and the status code. Next would have used it if the error had reached it.
A page does the same thing. redirect() inside try/catch in a server component returned 200 and rendered the rest of the page, with "NEXT_REDIRECT" logged by the catch. Without try/catch the same page returned 307 with the location header.
Fix 1: redirect after the try/catch
export async function save() {
try {
await db.insert(/* ... */);
} catch (e) {
return { error: "Couldn't save" };
}
redirect("/done");
}Redirected to /done. This is the cleanest fix: only the code that can really fail goes in try, and the redirect runs once it has succeeded. Return from the catch so a failed save doesn't fall through to the redirect.
Fix 2: unstable_rethrow at the top of catch
import { redirect, unstable_rethrow } from "next/navigation";
export async function save() {
try {
await db.insert(/* ... */);
redirect("/done");
} catch (e) {
unstable_rethrow(e); // rethrows NEXT_REDIRECT, notFound() and other Next errors
console.error(e); // only your own errors get here
}
}Redirected to /done. Use this when the redirect has to stay inside the try, for example in a helper deep in your code. It has to be the first line of the catch, before anything that logs or reports the error. It also passes through notFound() and the dynamic-rendering signals that try/catch can swallow in the same way.
Fix 3: try/finally is fine
redirect() inside try with only a finally block (no catch) redirected correctly, and the finally block still ran. If you only need cleanup, you don't need a catch at all.
What else I measured
where result
redirect() in a page 307, location header
permanentRedirect() in a page 308, location header
redirect() in a Route Handler 307, location header
redirect() to an external URL (action) browser landed on the external site
redirect() in a client onClick navigated, but logged "Uncaught Error: NEXT_REDIRECT"
router.push() in a client onClick navigated, no errorThe onClick case is the other place people see the raw error. redirect() is for rendering and Server Actions. In an event handler it still navigated in my test, but as an uncaught error, so error trackers like Sentry would report it every time. Use router.push() from useRouter for client navigation.
The status codes matter for SEO: redirect() gives a temporary 307, so for a page that has moved for good use permanentRedirect() (308). There's more on that in the redirects post linked below.
Quick check
- Seeing NEXT_REDIRECT in logs or Sentry? Something caught it. Search for try/catch around the code that calls redirect(), including in helpers it calls.
- Promise .catch() chains catch it too, the same as try/catch.
- In a client component, use router.push() or <Link>, not redirect().
- In proxy (middleware), use NextResponse.redirect(), not redirect().
All results above come from one Next.js 16.4.4 project built with next build and run with next start on Node 26. The browser checks were in Chrome.
Related
- Next.js redirects: 307 vs 308 for SEO
- Setting cookies in Next.js, tested
- Dynamic server usage: couldn't be rendered statically
- Next.js development