denoland/deno · error · TypeError
Cannot change header: headers are immutable
Error message
Cannot change header: headers are immutable
What it means
appendHeader step 3: when a Headers object's guard is 'immutable', any mutation throws. Headers attached to responses returned by fetch() are immutable per the Fetch spec, so set/append/delete on them fails.
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 89f33cbef2)
Solutions
- Rebuild the response mutably: const copy = new Response(res.body, res); copy.headers.set('x', 'y').
- Modify request headers before fetch instead of response headers after.
- Copy entries into a fresh Headers when you only need the values: new Headers(res.headers).
Example fix
// before
const res = await fetch(url);
res.headers.set('x-cache', 'miss'); // throws
// after
const res = await fetch(url);
const copy = new Response(res.body, res);
copy.headers.set('x-cache', 'miss'); Defensive patterns
Strategy: fallback
Try / catch
let h = res.headers;
try {
h.set('x-tag', 'v');
} catch (e) {
if (e instanceof TypeError && /immutable/.test(e.message)) {
const copy = new Response(res.body, res);
copy.headers.set('x-tag', 'v');
return copy;
}
throw e;
} Prevention
- Assume fetched response headers are read-only; plan mutation as reconstruction.
- Apply custom headers when building the Response you return, not on the one fetch gave you.
When it happens
Trigger: const res = await fetch(url); res.headers.set('x', 'y'); — also append or delete on fetched response headers.
Common situations: Middleware that rewrites downstream response headers; caching layers that tag responses after fetch returns.
Related errors
- Cannot change headers: headers are immutable
- Invalid header: length must be 2, but is ${header.length}
- Invalid header name: "${name}"
- Invalid header value: "${value}"
- The status provided (${init.status}) is not equal to 101 and
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/f1cc85c402e0137b.
Report an issue: GitHub.