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

  1. Move redirect/Response returns to the very top of the page frontmatter, before any markup or awaited render work.
  2. Perform auth/condition checks before returning any HTML so the Response is set before the stream starts.
  3. 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

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


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/620cd2daea185186. Report an issue: GitHub.