{"record":{"id":"b802a670cb7cc127","repo":"withastro/astro","slug":"responsesenterror-b802a6","errorCode":"ResponseSentError","errorMessage":"The response has already been sent to the browser and cannot be altered.","messagePattern":"The response has already been sent to the browser and cannot be altered\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/fetch/fetch-state.ts","lineNumber":573,"sourceCode":"\t\t\t},\n\t\t});\n\n\t\treturn Astro as AstroGlobal;\n\t}\n\n\t/**\n\t * Creates the Astro page-level partial (prototype for Astro global).\n\t */\n\tcreateAstroPagePartial(\n\t\tresult: SSRResult,\n\t\tapiContext: ActionAPIContext,\n\t): Omit<AstroGlobal, 'props' | 'self' | 'slots'> {\n\t\tconst state = this;\n\t\tconst { cookies, locals, params, logger, url } = this;\n\t\tconst { response } = result;\n\t\tconst redirect = (path: string, status = 302) => {\n\t\t\tif ((state.request as any)[responseSentSymbol]) {\n\t\t\t\tthrow new AstroError({\n\t\t\t\t\t...AstroErrorData.ResponseSentError,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn new Response(null, { status, headers: { Location: path } });\n\t\t};\n\n\t\tconst rewrite = async (reroutePayload: RewritePayload) => {\n\t\t\treturn await state.rewrite(reroutePayload);\n\t\t};\n\n\t\tconst callAction = createCallAction(apiContext);\n\n\t\tconst partial: Record<string, any> = {\n\t\t\tgenerator: ASTRO_GENERATOR,\n\t\t\troutePattern: this.routeData!.route,\n\t\t\tisPrerendered: this.routeData!.prerender,\n\t\t\tcookies,\n\t\t\tget clientAddress() {","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/core/fetch/fetch-state.ts#L555-L591","documentation":"The page-level Astro.redirect() checks a private responseSent marker on the request before building the redirect Response. Once streaming to the browser has started, the status and headers can no longer change, so redirect() throws AstroErrorData.ResponseSentError instead of silently doing nothing. This protects against half-sent pages that would otherwise redirect inconsistently.","triggerScenarios":"Calling Astro.redirect() after the first chunk of the response has been flushed: inside a component or slot rendered mid-stream, after an awaited slow data fetch while the shell is already streaming, or calling redirect a second time after the response began.","commonSituations":"Auth or permission checks that run after an expensive await inside a streamed page; layouts or islands that redirect conditionally but render after content has flushed; upgrading from buffered rendering to streaming and finding late redirects now fail.","solutions":["Do redirect decisions in the frontmatter before any markup is returned, or better, in middleware (context.redirect) before rendering starts","Move slow auth/session lookups ahead of the first flush point so the redirect verdict is known before streaming","If the redirect decision genuinely arrives after flush, emit an HTML meta-refresh or a small client-side redirect script instead of calling Astro.redirect()"],"exampleFix":"// before — decides after streaming began\nconst user = await slowAuthCheck();\nif (!user) return Astro.redirect('/login');\n\n// after — guard before rendering starts (middleware)\nexport const onRequest = async (context, next) => {\n  if (!context.cookies.get('session')) return context.redirect('/login', 302);\n  return next();\n};","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"import { AstroError } from 'astro/errors';\ntry {\n  return Astro.redirect('/login');\n} catch (err) {\n  if (err instanceof AstroError && err.code === 'ResponseSentError') {\n    // headers can no longer change — fall back to an HTML-level redirect\n    return new Response(\n      '<!doctype html><meta http-equiv=\"refresh\" content=\"0;url=/login\">',\n      { headers: { 'content-type': 'text/html' } }\n    );\n  }\n  throw err;\n}","preventionTips":["Make redirect decisions in middleware or at the top of frontmatter, before any markup is returned","Keep auth/session checks ahead of the first streaming chunk","Never call Astro.redirect() from components that may render mid-stream"],"tags":["redirect","streaming","response-lifecycle","astro-global"],"backgroundTag":"response-already-sent","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}