How to Fix: `next/navigation` `redirect` error with relative URL
The crash is not caused by your page logic—it happens because next/navigation redirect() is being given a relative URL that the current runtime path cannot safely resolve in this context, which leads to a redirect parsing failure instead of a clean navigation.
Table of Contents
Understanding the Root Cause
In modern Next.js, the redirect() helper from next/navigation is designed to throw a special internal redirect signal that Next.js intercepts and converts into navigation behavior. This works well when the target is a valid, fully resolvable path. The problem in this issue appears when a redirect target is passed as a relative reference such as ./somewhere or a path that depends on ambient URL resolution.
Unlike a browser anchor tag, redirect() does not always resolve relative URLs the way developers expect. In server-driven rendering flows, Next.js may evaluate the redirect in a context where only an absolute app path like /dashboard is safe and deterministic. A relative value can become ambiguous because resolution depends on the current request URL, route segment boundaries, trailing slash behavior, and runtime internals.
That is why you may see an error instead of a redirect: Next.js expects a redirect destination it can normalize immediately, and a relative target can fail that normalization step.
In short, the root cause is this:
- redirect() expects a stable destination.
- Relative URLs are context-dependent.
- In this execution path, Next.js does not reliably resolve that context for redirect normalization.
- The result is a thrown redirect error that surfaces as a runtime failure.
The reproduction linked in the issue demonstrates exactly that mismatch between expected browser-style relative navigation and actual server/runtime redirect semantics. You can review the sample project in the GitHub reproduction repository.
Step-by-Step Solution
The fix is straightforward: do not pass relative URLs to redirect(). Use an absolute app-relative pathname starting with /, or build a full URL explicitly when needed.
1. Identify the failing redirect
You likely have code similar to this:
import { redirect } from 'next/navigation'
redirect('./dashboard')
or:
import { redirect } from 'next/navigation'
redirect('login')
These forms are risky because they are relative.
2. Replace it with an absolute pathname
Use a root-based application path instead:
import { redirect } from 'next/navigation'
redirect('/dashboard')
For a login page:
import { redirect } from 'next/navigation'
redirect('/login')
3. Preserve query parameters explicitly when needed
If your redirect depends on dynamic data, compose the final path yourself:
import { redirect } from 'next/navigation'
const nextUrl = '/login?from=home'
redirect(nextUrl)
4. If you truly need URL-based resolution, construct it explicitly
When redirecting based on a request origin or complex path logic, create the URL using the standard URL API and then pass a string that Next.js can safely consume.
import { redirect } from 'next/navigation'
const url = new URL('/dashboard', 'http://localhost:3000')
redirect(url.pathname)
In most app code, though, the simplest and safest pattern is still:
redirect('/target')
5. For nested routes, still prefer absolute app paths
Even if the redirect happens deep inside a route tree, avoid assumptions like “go one level up” or “go to sibling route” via relative path syntax.
Instead of:
redirect('../settings')
Use:
redirect('/account/settings')
Working Redirect Patterns
Here are safe patterns you can use in real projects.
Redirect from a Server Component
import { redirect } from 'next/navigation'
export default function Page() {
redirect('/welcome')
}
Redirect after an auth check
import { redirect } from 'next/navigation'
export default async function ProtectedPage() {
const user = null
if (!user) {
redirect('/login')
}
return 'Protected content'
}
Redirect with a dynamic slug
import { redirect } from 'next/navigation'
export default async function Page() {
const slug = 'getting-started'
redirect(`/docs/${slug}`)
}
Redirect from a server action result
'use server'
import { redirect } from 'next/navigation'
export async function submitForm() {
redirect('/success')
}
Notice the common rule across all examples: the destination is an absolute pathname inside the app.
Common Edge Cases
Even after fixing the relative redirect bug, a few adjacent issues can still trip you up.
1. Base path deployments
If your app is deployed under a basePath, make sure your redirect target matches the effective route structure used by the app. Test redirects in both local and deployed environments.
2. Trailing slash configuration
If your project uses trailingSlash, inconsistent path construction can create subtle route mismatches. Prefer consistent route generation everywhere.
3. Redirecting to external domains
If the destination is outside your app, construct the full URL deliberately rather than relying on relative behavior. Be especially careful when the destination is user-controlled.
4. Using redirect() in the wrong place
redirect() throws internally by design. If you wrap it in generic error handling without understanding that behavior, you may accidentally interfere with Next.js navigation flow.
5. Confusing router navigation with server redirects
On the client, useRouter().push() and link-based navigation behave differently from server-side redirect(). A relative href in a link may work while a relative target passed to redirect() fails.
6. Middleware and route handlers
If you are redirecting in middleware or route handlers, use the redirect APIs intended for those layers, such as NextResponse.redirect(), and verify the URL format expected there.
FAQ
Can I use ./ or ../ with next/navigation redirect()?
No—at least not reliably for this scenario. The safest approach is to always pass an absolute app-relative path like /dashboard.
Why does a relative link work in the browser but redirect() fails?
A browser link is resolved against the current document URL. redirect() is a framework-level server/runtime mechanism, and its URL normalization rules are stricter. It does not behave like a plain anchor tag.
What is the best practice for dynamic redirects in Next.js?
Build the final destination explicitly and pass a stable string path to redirect(). For internal routes, prefer patterns like redirect(`/team/${id}`) instead of relative path composition.
Final Recommendation
If you hit the next/navigation redirect error with a relative URL, treat it as a framework contract issue rather than a routing typo. The durable fix is to standardize all redirect() calls on absolute internal paths. That keeps redirects deterministic across Server Components, Server Actions, nested routes, and different deployment environments.