withastro/astro · error · AstroError
AstroResponseHeadersReassigned
AstroResponseHeadersReassigned
Error message
Individual headers can be added to and removed from `Astro.response.headers`, but it must not be replaced with another instance of `Headers` altogether.
What it means
On the fetch-state-backed Astro global, Astro.response.headers is defined with a getter only; the setter always throws AstroErrorData.AstroResponseHeadersReassigned. Astro forbids replacing the Headers instance because the rest of the render pipeline (cookies, actions, routing) already holds a reference to that exact object. Individual headers may still be added, changed, or removed in place.
Solutions
- Mutate the existing instance in place: Astro.response.headers.set('name', 'value')
- If you built a Headers or plain object elsewhere, copy its entries onto Astro.response.headers with set/append instead of assigning
- Use Astro.response.status / statusText for the other response fields — only the headers instance is protected
Example fix
// before
Astro.response.headers = new Headers({ 'x-cache': 'miss' });
// after
Astro.response.headers.set('x-cache', 'miss'); Defensive patterns
Strategy: validation
Validate before calling
function applyResponseHeaders(source: Headers | Record<string, string>): void {
const target = Astro.response.headers;
if (source instanceof Headers) {
for (const [name, value] of source) target.set(name, value);
} else {
for (const [name, value] of Object.entries(source)) target.set(name, value);
}
}
// use: applyResponseHeaders(new Headers({ 'x-cache': 'miss' })) instead of assigning Prevention
- Treat Astro.response.headers as read-only reference; only call set/append/delete/append on it
- Build headers in helpers that return plain objects and merge them with the pattern above
- Grep the codebase for 'Astro.response.headers =' after porting code from other frameworks
When it happens
Trigger: Any whole-object assignment in .astro frontmatter, middleware, or actions: Astro.response.headers = new Headers({...}), or assigning a Headers object built elsewhere (a helper function that returns new Headers() and is then assigned).
Common situations: Porting code from Express/Next-style frameworks where replacing the response or its headers is idiomatic; refactors that build a Headers object in a utility and assign it on the Astro global; copy-pasted snippets from tutorials targeting other stacks.
Related errors
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/3156b2d1b99a0495.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/fetch/fetch-state.ts:441
}
}
const componentMetadata =
(await env.componentMetadata(manifest, routeData)) ?? manifest.componentMetadata;
const headers = new Headers({ 'Content-Type': 'text/html' });
const partial = typeof this.partial === 'boolean' ? this.partial : Boolean(mod.partial);
const actionResult = hasActionPayload(this.locals)
? deserializeActionResult(this.locals._actionPayload.actionResult)
: undefined;
const status = this.status;
const response = {
status: actionResult?.error ? actionResult?.error.status : status,
statusText: actionResult?.error ? actionResult?.error.type : 'OK',
get headers() {
return headers;
},
set headers(_) {
throw new AstroError(AstroErrorData.AstroResponseHeadersReassigned);
},
} satisfies AstroGlobal['response'];
const state = this;
const result: SSRResult = {
base: manifest.base,
userAssetsBase: manifest.userAssetsBase,
cancelled: false,
clientDirectives,
inlinedScripts,
componentMetadata,
compressHTML,
cookies: this.cookies,
createAstro: (props, slots) => state.createAstro(result, props, slots, ctx),
links,
// SAFETY: createResult is only called after route resolution, so routeData
// is always set and the params getter always returns a value.
params: this.params!,View on GitHub (pinned to e294953aa8)