withastro/astro · error · AstroError
ResponseSentError
ResponseSentError
Error message
The response has already been sent to the browser and cannot be altered.
What it means
`Astro.cookies.set()` serializes the cookie into the outgoing map, then checks the internal `Symbol.for('astro.responseSent')` flag on the request. Once the response pipeline has marked that flag (in `prepare-response` or the dev server), headers can no longer be altered and `set()` throws `ResponseSentError`. A mere `console.warn` fires when cookies were consumed (e.g. set inside an imported component); the throw is specifically for after the response was actually sent.
Solutions
- Move `Astro.cookies.set()` before any `return`/`Astro.redirect()` in the same frontmatter block.
- Set cookies in middleware (`onRequest`) or in the page frontmatter — never in imported components or slots.
- If the value only becomes known late, restructure the route: do the async work first, then set the cookie, then return the response.
- As a last resort in library code, catch the error and degrade to logging instead of crashing the render.
Example fix
// before — set() runs after the redirect response was sent
if (!user) return Astro.redirect('/login');
Astro.cookies.set('lastPath', Astro.url.pathname); // throws ResponseSentError
// after — set cookies before returning the response
Astro.cookies.set('lastPath', Astro.url.pathname);
if (!user) return Astro.redirect('/login'); Defensive patterns
Strategy: type-guard
Validate before calling
// Mirror the internal flag Astro sets on the request once the response leaves
const responseSent = Reflect.get(Astro.request as object, Symbol.for('astro.responseSent')) === true;
if (!responseSent) {
Astro.cookies.set('session', token);
} Type guard
// True while Set-Cookie headers can still be emitted
function canStillSetCookies(request: Request): boolean {
return Reflect.get(request, Symbol.for('astro.responseSent')) !== true;
} Try / catch
try {
Astro.cookies.set('flag', '1');
} catch (err) {
if (err instanceof Error && err.name === 'ResponseSentError') {
logger.warn('Cookie dropped: response already sent');
return;
}
throw err;
} Prevention
- Only call `Astro.cookies.set()` in page frontmatter or middleware `onRequest`, before any `return` or `Astro.redirect()`.
- Never set cookies in imported components or layouts rendered after headers flush.
- Order route code as: async work → set cookies → return response/redirect.
- Test login/redirect flows in dev, which exercises the sent-response path.
When it happens
Trigger: Calling `Astro.cookies.set()` after returning/redirecting (e.g. after `return Astro.redirect('/login')`), inside a component rendered after headers flushed, or during streaming when the body has already started and some awaited code then sets a cookie.
Common situations: Login/auth flows that set a session cookie after the redirect statement; cookie logic placed in a layout or imported component instead of the page frontmatter; slow async work in a streamed page that tries to set cookies at the end.
Related errors
- Astro.cookies.set() was called after the cookies had…
- ResponseSentError
- ResponseSentError
- A collision will result in a hard error in following…
- ActionCalledFromServerError
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/25602c345dadfad6.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/cookies/cookies.ts:210
}
const { encode, ...attributes } = options ?? {};
this.#ensureOutgoingMap().set(key, [
serializedValue,
stringifySetCookie(
{
...attributes,
name: key,
value: serializedValue,
},
{ encode },
),
true,
]);
if ((this.#request as any)[responseSentSymbol]) {
throw new AstroError({
...AstroErrorData.ResponseSentError,
});
}
}
/**
* Merges a new AstroCookies instance into the current instance. Any new cookies
* will be added to the current instance, overwriting any existing cookies with the same name.
*/
merge(cookies: AstroCookies) {
const outgoing = cookies.#outgoing;
if (outgoing) {
for (const [key, value] of outgoing) {
this.#ensureOutgoingMap().set(key, value);
}
}
}
View on GitHub (pinned to e294953aa8)