withastro/astro · error · AstroError
ResponseSentError
ResponseSentError
Error message
The response has already been sent to the browser and cannot be altered.
What it means
In the streaming render destination, each chunk is written to the browser as it's produced. If a chunk is itself a `Response` (typically a redirect produced eagerly inside the component tree), it arrives after the first page chunk was already enqueued — meaning headers/status have already been committed. Astro cannot honor a late Response, so it throws `ResponseSentError`.
Source
Thrown at packages/astro/src/runtime/server/render/astro/render.ts:96
return new ReadableStream({
start(controller) {
const destination: RenderDestination = {
write(chunk) {
// Automatic doctype insertion for pages
if (isPage && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
controller.enqueue(encoder.encode(doctype));
}
}
// `chunk` might be a Response that contains a redirect,
// that was rendered eagerly and therefore bypassed the early check
// whether headers can still be modified. In that case, throw an error
if (chunk instanceof Response) {
throw new AstroError({
...AstroErrorData.ResponseSentError,
});
}
const bytes = chunkToByteArray(result, chunk);
controller.enqueue(bytes);
},
};
(async () => {
try {
await renderStreaming(templateResult, result, destination);
controller.close();
} catch (e) {
// We don't have a lot of information downstream, and upstream we can't catch the error properly
// So let's add the location here
if (AstroError.is(e) && !e.loc) {
e.setLocation({View on GitHub (pinned to d081033d5f)
Solutions
- Move redirect/Response returns to the very top of the page frontmatter, before any markup or awaited render work.
- Perform auth/condition checks before returning any HTML so the Response is set before the stream starts.
- Avoid returning a Response from inside child components or slots of a streaming page.
Example fix
// before — redirect after partial render in a streaming page
---
<html><body>Hello</body></html>
{needsLogin ? Astro.redirect('/login') : null}
---
// after — redirect before any output
---
if (needsLogin) return Astro.redirect('/login');
---
<html><body>Hello</body></html> Defensive patterns
Strategy: validation
Validate before calling
// Page-level discipline: return Response before rendering
// (no runtime guard possible once the stream has flushed)
export default async function (astro: APIContext): Promise<Response | void> {
if (await needsRedirect(astro)) return Astro.redirect('/elsewhere');
// render only after all Response decisions are made
} Prevention
- Put every redirect/auth check at the very top of page frontmatter, before any markup.
- Never return a Response from nested components or slots in a streaming page.
- Treat 'Response before output' as a hard ordering rule.
When it happens
Trigger: A `Response`/redirect returned from deep inside a streaming page after some HTML already flushed (e.g. inside a slotted child, an async island, or a component rendered after partial output); `return Astro.redirect(...)` placed after early rendering work in a streamed page.
Common situations: Conditional redirect logic placed mid-page instead of at the top of frontmatter; redirects emitted from nested components/slots in on-demand streaming pages; auth checks that run after some content renders.
Related errors
- ResponseSentError
- RedirectWithNoLocation
- OnlyResponseCanBeReturned
- Unable to render ${result.pathname} because it contains an u
- ActionsReturnedInvalidDataError
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/620cd2daea185186.
Report an issue: GitHub.