{"record":{"id":"45a03e27a9ee5589","repo":"withastro/astro","slug":"redirectwithnolocation","errorCode":"RedirectWithNoLocation","errorMessage":"A redirect must be given a location with the `Location` header.","messagePattern":"A redirect must be given a location with the `Location` header\\.","errorType":"validation","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/redirects/validate.ts","lineNumber":7,"sourceCode":"import { AstroError, AstroErrorData } from '../errors/index.js';\n\nexport function getRedirectLocationOrThrow(headers: Headers): string {\n\tlet location = headers.get('location');\n\n\tif (!location) {\n\t\tthrow new AstroError({\n\t\t\t...AstroErrorData.RedirectWithNoLocation,\n\t\t});\n\t}\n\n\treturn location;\n}\n","sourceCodeStart":1,"sourceCodeEnd":14,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/core/redirects/validate.ts#L1-L14","documentation":"Thrown by getRedirectLocationOrThrow() when a Response with a 3xx redirect status lacks a 'location' header. Astro requires every redirect Response to carry a Location header so the browser knows where to navigate. Without it the redirect is meaningless and the runtime refuses to serve it.","triggerScenarios":"Returning a Response with status 301/302/307/308 but no 'Location' header; calling Astro.redirect() and then mutating the Response to strip the header; manually constructing new Response(null, { status: 302 }) without headers; an integration or middleware replacing response headers.","commonSituations":"Manually building a redirect Response in an endpoint or page instead of using Astro.redirect(url); middleware that clones a redirect Response and drops headers; adapter/proxy that strips Location before Astro reads it.","solutions":["Use Astro.redirect('/path') which sets the Location header automatically.","If building the Response manually, always pass headers: { Location: '/target' }.","Inspect middleware/onRequest handlers that touch Astro.response or the returned Response to ensure they preserve Location.","Log response.headers.get('location') right before returning to confirm the header is present."],"exampleFix":"// before\nreturn new Response(null, { status: 302 });\n\n// after\nreturn new Response(null, { status: 302, headers: { Location: '/login' } });\n// or simply\nreturn Astro.redirect('/login');","handlingStrategy":"validation","validationCode":"function hasRedirectLocation(response: Response): boolean {\n  return Boolean(response.headers.get('location'));\n}\n// before returning a redirect:\nconst res = new Response(null, { status: 302, headers: { Location: '/x' } });\nif (!hasRedirectLocation(res)) throw new Error('missing Location');","typeGuard":"function isRedirectWithLocation(res: Response): res is Response & { headers: Headers } {\n  return res.status >= 300 && res.status < 400 && res.headers.has('location');\n}","tryCatchPattern":"try {\n  const loc = getRedirectLocationOrThrow(response.headers);\n} catch (e) {\n  // ensure Location is set before retrying\n  response.headers.set('Location', '/fallback');\n}","preventionTips":["Prefer Astro.redirect(url) which always sets Location.","In tests, assert response.headers.has('location') for every redirect.","Audit middleware that touches the returned Response to preserve Location."],"tags":["redirect","http-headers","ssr","response"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}