{"record":{"id":"f1cc85c402e0137b","repo":"denoland/deno","slug":"cannot-change-header-headers-are-immutable","errorCode":null,"errorMessage":"Cannot change header: headers are immutable","messagePattern":"Cannot change header: headers are immutable","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/20_headers.js","lineNumber":181,"sourceCode":" * @param {Headers} headers\n * @param {string} name\n * @param {string} value\n */\nfunction appendHeader(headers, name, value) {\n  // 1.\n  value = normalizeHeaderValue(value);\n\n  // 2.\n  if (!checkHeaderNameForHttpTokenCodePoint(name)) {\n    throw new TypeError(`Invalid header name: \"${name}\"`);\n  }\n  if (!checkForInvalidValueChars(value)) {\n    throw new TypeError(`Invalid header value: \"${value}\"`);\n  }\n\n  // 3.\n  if (headers[_guard] == \"immutable\") {\n    throw new TypeError(\"Cannot change header: headers are immutable\");\n  }\n\n  // 7.\n  const list = headerListFromHeaders(headers);\n  const lowerNames = ensureLowerNames(headers);\n  const lowercaseName = byteLowerCase(name);\n  for (let i = 0; i < lowerNames.length; i++) {\n    if (lowerNames[i] === lowercaseName) {\n      name = list[i][0];\n      break;\n    }\n  }\n  ArrayPrototypePush(list, [name, value]);\n  ArrayPrototypePush(lowerNames, lowercaseName);\n}\n\nfunction appendHeaderToList(list, name, value) {\n  value = normalizeHeaderValue(value);","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/fetch/20_headers.js#L163-L199","documentation":"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.","triggerScenarios":"const res = await fetch(url); res.headers.set('x', 'y'); — also append or delete on fetched response headers.","commonSituations":"Middleware that rewrites downstream response headers; caching layers that tag responses after fetch returns.","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)."],"exampleFix":"// before\nconst res = await fetch(url);\nres.headers.set('x-cache', 'miss'); // throws\n// after\nconst res = await fetch(url);\nconst copy = new Response(res.body, res);\ncopy.headers.set('x-cache', 'miss');","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"let h = res.headers;\ntry {\n  h.set('x-tag', 'v');\n} catch (e) {\n  if (e instanceof TypeError && /immutable/.test(e.message)) {\n    const copy = new Response(res.body, res);\n    copy.headers.set('x-tag', 'v');\n    return copy;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["headers","fetch","immutability","response"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}