{"record":{"id":"4b56f204acf7eb9e","repo":"denoland/deno","slug":"err-http-headers-sent-4b56f2","errorCode":"ERR_HTTP_HEADERS_SENT","errorMessage":"Cannot set headers after they are sent to the client","messagePattern":"Cannot set headers after they are sent to the client","errorType":"exception","errorClass":"NodeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_http_outgoing.ts","lineNumber":331,"sourceCode":"      if (this.socket) {\n        this.socket.destroy(error);\n      } else {\n        this.once(\"socket\", function socketDestroyOnConnect(socket: any) {\n          socket.destroy(error);\n        });\n      }\n\n      return this;\n    },\n    writable: true,\n    enumerable: true,\n    configurable: true,\n  },\n  setHeader: {\n    __proto__: null,\n    value: function setHeader(name: string, value: string) {\n      if (this._header) {\n        throw new ERR_HTTP_HEADERS_SENT(\"set\");\n      }\n      validateHeaderName(name);\n      validateHeaderValue(name, value);\n\n      let headers = this[kOutHeaders];\n      if (headers === null) {\n        this[kOutHeaders] = headers = ObjectCreate(null);\n      }\n\n      name = StringPrototypeToString(name);\n      headers[StringPrototypeToLowerCase(name)] = [name, value];\n      return this;\n    },\n    writable: true,\n    enumerable: true,\n    configurable: true,\n  },\n  appendHeader: {","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_http_outgoing.ts#L313-L349","documentation":"OutgoingMessage#setHeader refuses to operate once this._header is set — i.e. after the header block has been serialized for sending. The 'Cannot set headers after they are sent to the client' ERR_HTTP_HEADERS_SENT with context 'set' fires before any name/value validation. _header is populated by the first _send/_storeHeader, which happens on the first write()/end() or explicit head-flush.","triggerScenarios":"res.setHeader('x-request-id', id) inside a 'finish' listener; error handlers that set headers after the response body already started streaming; calling setHeader after end() was invoked.","commonSituations":"Express-style middleware that sets headers after a route handler already called res.send(); async work (logging, metrics) completing after the response flushed and then touching headers; retry wrappers that mutate headers per attempt on the same response object.","solutions":["Move all setHeader calls before the first write/end/send","Guard with if (!res.headersSent) { res.setHeader(...) }","For late metadata, use trailers (addTrailers) instead of headers when the protocol allows"],"exampleFix":"// before\nres.end(body);\nres.setHeader('x-trace', traceId); // throws\n\n// after\nres.setHeader('x-trace', traceId);\nres.end(body);","handlingStrategy":"validation","validationCode":"function safeSetHeader(res, name, value) {\n  if (!res.headersSent) res.setHeader(name, value);\n  else debug('header too late:', name);\n}\nsafeSetHeader(res, 'x-request-id', id);","typeGuard":"const headersMutable = (msg) => !msg.headersSent && !msg.finished;","tryCatchPattern":"try { res.setHeader(name, value); } catch (e) { if (e.code === 'ERR_HTTP_HEADERS_SENT') { /* too late: log and continue */ } else throw e; }","preventionTips":["Set every header before the first write/end/send","Audit async middleware for post-flush header mutation","Use headersSent as the guard in shared helpers"],"tags":["http","headers","server","lifecycle","node-compat"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}