denoland/deno · error · NodeTypeError
ERR_INVALID_HTTP_TOKEN
ERR_INVALID_HTTP_TOKEN
Error message
Trailer name must be a valid HTTP token ["${field}"] What it means
OutgoingMessage#addTrailers iterates the supplied headers object (or [name, value] pairs) and requires every field name to be a non-empty string that passes checkIsHttpToken — the RFC 7230 token charset. Failing names (including non-strings such as numbers from object keys, empty strings, or names with separators/unicode) throw ERR_INVALID_HTTP_TOKEN for 'Trailer name'. The trailer string is assembled into this._trailer for the terminating chunk.
Source
Thrown at ext/node/polyfills/_http_outgoing.ts:542
configurable: true,
},
addTrailers: {
__proto__: null,
value: function addTrailers(headers: any) {
this._trailer = "";
const keys = ObjectKeys(headers);
const isArray = ArrayIsArray(headers);
let field, value;
for (let i = 0, l = keys.length; i < l; i++) {
if (isArray) {
field = headers[keys[i]][0];
value = headers[keys[i]][1];
} else {
field = keys[i];
value = headers[field];
}
if (typeof field !== "string" || !field || !checkIsHttpToken(field)) {
throw new ERR_INVALID_HTTP_TOKEN("Trailer name", field);
}
if (checkInvalidHeaderChar(value)) {
debug('Trailer "%s" contains invalid characters', field);
throw new ERR_INVALID_CHAR("trailer content", field);
}
this._trailer += field + ": " + value + "\r\n";
}
},
writable: true,
enumerable: true,
configurable: true,
},
end: {
__proto__: null,
value: function end(chunk: any, encoding: any, callback: any) {
if (typeof chunk === "function") {
callback = chunk;
chunk = null;View on GitHub (pinned to 89f33cbef2)
Solutions
- Use fixed, well-formed trailer names: 'content-md5', 'x-trace-id'
- Validate dynamic names against /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/ before addTrailers
- If the name cannot be a token, put the value in the body or a header instead of a trailer
Example fix
// before
res.addTrailers({ [metricName]: value }); // metricName = 'resp time'
// after
const TOKEN_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
const name = TOKEN_RE.test(metricName) ? metricName : 'x-metric';
res.addTrailers({ [name]: value }); Defensive patterns
Strategy: validation
Validate before calling
const HTTP_TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
function validTrailerNames(t) {
return Object.keys(t).every((k) => HTTP_TOKEN.test(k));
}
if (validTrailerNames(trailers)) res.addTrailers(trailers); Type guard
const isTrailerTokenName = (n) => typeof n === 'string' && n.length > 0 && /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(n);
Try / catch
try { res.addTrailers(trailers); } catch (e) { if (e.code === 'ERR_INVALID_HTTP_TOKEN') { /* drop bad names or send plain headers */ } else throw e; } Prevention
- Use a fixed set of trailer names
- Never derive trailer names from user data
- Validate dynamic keys with the token regex before addTrailers
When it happens
Trigger: res.addTrailers({ 'Content-MD5 ': hash }) (trailing space); addTrailers(obj) where obj keys came from JSON with numeric-looking names; header names containing spaces or non-ASCII from dynamic generation.
Common situations: Generating trailer names from data (checksums, counters) without validating the charset; porting header objects whose keys are coerced by JSON parsing; copy-paste typos with invisible whitespace.
Related errors
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/0e78a03270e67989.
Report an issue: GitHub.