denoland/deno · error · TypeError
Cannot change header: headers are immutable
Error message
Cannot change header: headers are immutable
What it means
Headers objects carry a guard; when it is "immutable" - most notably on responses returned by fetch() - every mutation method that goes through appendHeader (append, set) throws. The name/value checks run first, so a bad name on an immutable header reports the name error instead.
Solutions
- Copy into a mutable Headers: `const h = new Headers(res.headers); h.set("x", "y")`
- Rebuild the response: `new Response(res.body, { status: res.status, headers: h })`
- Set custom headers when constructing a new Response instead of mutating a received one
Example fix
// before
const res = await fetch(url);
res.headers.set("x-cache", "miss"); // immutable -> throws
// after
const res = await fetch(url);
const headers = new Headers(res.headers);
headers.set("x-cache", "miss");
const out = new Response(res.body, { status: res.status, headers }); Defensive patterns
Strategy: fallback
Validate before calling
function mutableCopy(res: Response): Headers {
const h = new Headers(res.headers); // fresh list, guard 'none'
return h;
} Prevention
- Never mutate headers of a response you did not construct
- Copy into new Headers(...) before editing, then rebuild with new Response(res.body, { headers })
- Set custom headers at construction time rather than after fetch()
When it happens
Trigger: `(await fetch(url)).headers.set("x", "y")`; calling append/delete on headers of a received response.
Common situations: Middleware trying to add auth or CORS headers to a fetched response before returning it; frameworks mutating a response they did not construct.
Related errors
- Invalid header: length must be 2, but is
- Invalid header name
- Cannot change headers: headers are immutable
- Cannot read headers: request closed
- Invalid header value
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/f1cc85c402e0137b.
Report an issue: GitHub.
Appendix: source
Thrown at ext/fetch/20_headers.js:181
* @param {Headers} headers
* @param {string} name
* @param {string} value
*/
function appendHeader(headers, name, value) {
// 1.
value = normalizeHeaderValue(value);
// 2.
if (!checkHeaderNameForHttpTokenCodePoint(name)) {
throw new TypeError(`Invalid header name: "${name}"`);
}
if (!checkForInvalidValueChars(value)) {
throw new TypeError(`Invalid header value: "${value}"`);
}
// 3.
if (headers[_guard] == "immutable") {
throw new TypeError("Cannot change header: headers are immutable");
}
// 7.
const list = headerListFromHeaders(headers);
const lowerNames = ensureLowerNames(headers);
const lowercaseName = byteLowerCase(name);
for (let i = 0; i < lowerNames.length; i++) {
if (lowerNames[i] === lowercaseName) {
name = list[i][0];
break;
}
}
ArrayPrototypePush(list, [name, value]);
ArrayPrototypePush(lowerNames, lowercaseName);
}
function appendHeaderToList(list, name, value) {
value = normalizeHeaderValue(value);View on GitHub (pinned to 9ad36f7a2c)