denoland/deno · error · NodeError
ERR_HTTP_HEADERS_SENT
ERR_HTTP_HEADERS_SENT
Error message
Cannot set headers after they are sent to the client
What it means
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.
Source
Thrown at ext/node/polyfills/_http_outgoing.ts:331
if (this.socket) {
this.socket.destroy(error);
} else {
this.once("socket", function socketDestroyOnConnect(socket: any) {
socket.destroy(error);
});
}
return this;
},
writable: true,
enumerable: true,
configurable: true,
},
setHeader: {
__proto__: null,
value: function setHeader(name: string, value: string) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT("set");
}
validateHeaderName(name);
validateHeaderValue(name, value);
let headers = this[kOutHeaders];
if (headers === null) {
this[kOutHeaders] = headers = ObjectCreate(null);
}
name = StringPrototypeToString(name);
headers[StringPrototypeToLowerCase(name)] = [name, value];
return this;
},
writable: true,
enumerable: true,
configurable: true,
},
appendHeader: {View on GitHub (pinned to 89f33cbef2)
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
Example fix
// before
res.end(body);
res.setHeader('x-trace', traceId); // throws
// after
res.setHeader('x-trace', traceId);
res.end(body); Defensive patterns
Strategy: validation
Validate before calling
function safeSetHeader(res, name, value) {
if (!res.headersSent) res.setHeader(name, value);
else debug('header too late:', name);
}
safeSetHeader(res, 'x-request-id', id); Type guard
const headersMutable = (msg) => !msg.headersSent && !msg.finished;
Try / catch
try { res.setHeader(name, value); } catch (e) { if (e.code === 'ERR_HTTP_HEADERS_SENT') { /* too late: log and continue */ } else throw e; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ERR_HTTP_HEADERS_SENT
- ERR_INVALID_ARG_TYPE
- ERR_HTTP_SOCKET_ASSIGNED
- ERR_HTTP_HEADERS_SENT
- Invalid header name: "${name}"
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/4b56f204acf7eb9e.
Report an issue: GitHub.