denoland/deno · error · NodeError
ERR_HTTP_TRAILER_INVALID
ERR_HTTP_TRAILER_INVALID
Error message
Trailers are invalid with this transfer encoding
What it means
RFC 7230 permits trailer fields only after a body that uses chunked transfer coding. During header serialization (_storeHeader) the polyfill checks `this.chunkedEncoding !== true && state.trailer` and throws ERR_HTTP_TRAILER_INVALID, because a non-chunked message ends at the first empty line after the header block and has no place to put trailers.
Source
Thrown at ext/node/polyfills/_http_outgoing.ts:1051
this.chunkedEncoding = true;
} else {
// We should only be able to get here if both Content-Length and
// Transfer-Encoding are removed by the user.
// See: test/parallel/test-http-remove-header-stays-removed.js
debug("Both Content-Length and Transfer-Encoding are removed");
// We can't keep alive in this case, because with no header info the body
// is defined as all data until the connection is closed.
this._last = true;
}
}
// Test non-chunked message does not have trailer header set,
// message will be terminated by the first empty line after the
// header fields, regardless of the header fields present in the
// message, and thus cannot contain a message body or 'trailers'.
if (this.chunkedEncoding !== true && state.trailer) {
throw new ERR_HTTP_TRAILER_INVALID();
}
const { header } = state;
this._header = header + "\r\n";
// Wait until the first body chunk, or close(), is sent to flush,
// UNLESS we're sending Expect: 100-continue.
if (state.expect) this._send("");
},
writable: true,
enumerable: true,
configurable: true,
},
_storeHeaderEntry: {
__proto__: null,
value: function _storeHeaderEntry(state: any, field: string, value: any) {
if (ArrayIsArray(value)) {
// RFC 6265: join multiple Cookie values with '; 'View on GitHub (pinned to 89f33cbef2)
Solutions
- Remove the Trailer header and the addTrailers() call when the transfer coding is not chunked
- Let the message be chunked: keep HTTP/1.1 and do not set Content-Length (call res.removeContentLength() if a framework set it for you)
- If you control the client, upgrade it to HTTP/1.1 so chunked encoding is negotiated
Example fix
// before
res.setHeader('Content-Length', 5);
res.setHeader('Trailer', 'X-Sum');
res.end('hello'); // throws ERR_HTTP_TRAILER_INVALID
// after (chunked)
res.removeHeader('Content-Length');
res.setHeader('Trailer', 'X-Sum');
res.write('hello');
res.addTrailers({ 'X-Sum': '5' });
res.end(); Defensive patterns
Strategy: validation
Validate before calling
// only promise trailers when the message is actually chunked
const canTrailer = res.chunkedEncoding === true;
if (canTrailer) res.setHeader('Trailer', 'X-Sum');
// ... later
if (canTrailer) res.addTrailers({ 'X-Sum': String(sum) }); Try / catch
try {
res.setHeader('Trailer', 'X-Metadata');
res.end(body);
} catch (e) {
if (e?.code === 'ERR_HTTP_TRAILER_INVALID') {
res.removeHeader('Trailer');
res.end(body); // retry without trailers
} else throw e;
} Prevention
- Never set Trailer together with Content-Length on the same message
- Gate trailers on chunkedEncoding, which is only true for HTTP/1.1 chunked responses
- Put trailer metadata in a header or the body when you cannot guarantee chunked coding
When it happens
Trigger: Setting `res.setHeader('Trailer', 'X-Metadata')` or calling res.addTrailers() on a message that will not be chunked: serving an HTTP/1.0 client (no chunked support), a response with an explicit Content-Length (identity coding), or a message where chunkedEncoding was disabled.
Common situations: Testing trailer support against an HTTP/1.0 client or a server/framework that always sets Content-Length; gRPC-style metadata patterns applied to plain non-chunked responses; enabling trailers without removing Content-Length.
Related errors
- ERR_INVALID_PROTOCOL
- Unsupported 'alpnProtocols' option provided. 'h2' and 'http/
- ERR_INVALID_ARG_TYPE
- ERR_INVALID_URL
- ERR_UNESCAPED_CHARACTERS
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/744304aa481a09dc.
Report an issue: GitHub.