withastro/astro · error · AstroError
RedirectWithNoLocation
RedirectWithNoLocation
Error message
A redirect must be given a location with the `Location` header.
What it means
During a static build, when a prerendered route returns a 3xx response Astro writes an HTML redirect stub and must know where to point it, so `getRedirectLocationOrThrow` reads the `Location` header and throws `RedirectWithNoLocation` (an AstroError) when it is null. The helper is invoked from the build's response handling (`core/build/generate.ts`), so this surfaces at build time for prerendered pages/endpoints that return a redirect status without a destination.
Solutions
- Use `Response.redirect(new URL('/target', context.url), 302)` which always sets `Location`
- Or add the header explicitly: `new Response(body, { status: 302, headers: { Location: '/target' } })`
- If forwarding an upstream redirect, propagate its `Location` header (or just return the upstream Response)
- Cover every 3xx branch in middleware with a header — including fallback/else paths
Example fix
// before
return new Response('Redirecting…', { status: 302 });
// after
return Response.redirect(new URL('/new-home', context.url), 302);
// or: new Response('Redirecting…', { status: 302, headers: { Location: '/new-home' } }) Defensive patterns
Strategy: type-guard
Validate before calling
// before returning a 3xx from a prerendered page/endpoint/middleware
if (response.status >= 300 && response.status < 400) {
if (!isRedirectWithLocation(response)) {
response = Response.redirect(new URL('/fallback', context.url), response.status);
}
} Type guard
// A 3xx is only a usable redirect if it carries a Location header
export function isRedirectWithLocation(res: Response): boolean {
return res.status >= 300 && res.status < 400 && res.headers.get('location') !== null;
} Try / catch
// Middleware safety net during prerender: never let a bare 3xx reach the build
export const onRequest = async (context, next) => {
const res = await next();
if (res?.status >= 300 && res?.status < 400 && !res.headers.get('location')) {
return Response.redirect(new URL('/', context.url), res.status);
}
return res;
}; Prevention
- Prefer `Response.redirect(url, status)` — it cannot produce a location-less redirect
- When forwarding upstream responses, forward headers too (or return the upstream Response whole)
- Unit-test middleware branches that return 3xx statuses, asserting `Location` presence
When it happens
Trigger: A prerendered page or endpoint returning `new Response(body, { status: 302 })` with no `Location` header; middleware (running during prerender) returning a bare 3xx status without location; code that copies a Response's status but drops its headers before returning it.
Common situations: Hand-built redirect Responses instead of `Response.redirect()`; an upstream fetch returning 3xx and the handler forwarding only `response.status`; middleware logic branches that set status but forget the header for some routes.
Related errors
- Failed to get static images from the Cloudflare prerender…
- Failed to get static paths from the Cloudflare prerender…
- Failed to prerender
- PrerenderDynamicEndpointPathCollide
- the prerender server responded
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/45a03e27a9ee5589.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/redirects/validate.ts:7
import { AstroError, AstroErrorData } from '../errors/index.js';
export function getRedirectLocationOrThrow(headers: Headers): string {
let location = headers.get('location');
if (!location) {
throw new AstroError({
...AstroErrorData.RedirectWithNoLocation,
});
}
return location;
}
View on GitHub (pinned to 52e6c34790)