{"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":"exception","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/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/fetch/20_headers.js#L163-L199","documentation":"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.","triggerScenarios":"`(await fetch(url)).headers.set(\"x\", \"y\")`; calling append/delete on headers of a received response.","commonSituations":"Middleware trying to add auth or CORS headers to a fetched response before returning it; frameworks mutating a response they did not construct.","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"],"exampleFix":"// before\nconst res = await fetch(url);\nres.headers.set(\"x-cache\", \"miss\"); // immutable -> throws\n\n// after\nconst res = await fetch(url);\nconst headers = new Headers(res.headers);\nheaders.set(\"x-cache\", \"miss\");\nconst out = new Response(res.body, { status: res.status, headers });","handlingStrategy":"fallback","validationCode":"function mutableCopy(res: Response): Headers {\n  const h = new Headers(res.headers); // fresh list, guard 'none'\n  return h;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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()"],"tags":["fetch","headers","immutability","web-api"],"backgroundTag":"immutable-response-headers","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}