denoland/deno · error · NodeTypeError
ERR_INVALID_HTTP_TOKEN
ERR_INVALID_HTTP_TOKEN
Error message
Header name must be a valid HTTP token ["${name}"] What it means
http2 compat validates every header passing through Http2ServerResponse.setHeader/appendHeader/setTrailer/writeHead via assertValidHeader. A header name that is empty, not a string, or contains a space is rejected with ERR_INVALID_HTTP_TOKEN because HTTP header names must be RFC 7230 tokens (alphanumerics and !#$%&'*+-.^_`|~).
Source
Thrown at ext/node/polyfills/internal/http2/compat.js:99
const kRawTrailers = Symbol("rawTrailers");
const kSetHeader = Symbol("setHeader");
const kAppendHeader = Symbol("appendHeader");
const kAborted = Symbol("aborted");
let statusMessageWarned = false;
let statusConnectionHeaderWarned = false;
// Defines and implements an API compatibility layer on top of the core
// HTTP/2 implementation, intended to provide an interface that is as
// close as possible to the current require('http') API
const assertValidHeader = hideStackFrames((name, value) => {
if (
name === "" ||
typeof name !== "string" ||
StringPrototypeIncludes(name, " ")
) {
throw new ERR_INVALID_HTTP_TOKEN.HideStackFramesError("Header name", name);
}
if (isPseudoHeader(name)) {
throw new ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED.HideStackFramesError();
}
if (value === undefined || value === null) {
throw new ERR_HTTP2_INVALID_HEADER_VALUE.HideStackFramesError(value, name);
}
if (!isConnectionHeaderAllowed(name, value)) {
connectionHeaderMessageWarn();
}
});
function isPseudoHeader(name) {
switch (name) {
case HTTP2_HEADER_STATUS: // :status
case HTTP2_HEADER_METHOD: // :method
case HTTP2_HEADER_PATH: // :path
case HTTP2_HEADER_AUTHORITY: // :authorityView on GitHub (pinned to 9ad36f7a2c)
Solutions
- Sanitize dynamic names to token characters: /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/ — replace or drop invalid ones
- Map user-controlled values onto a fixed allow-list of header names instead of using them as names
- Coerce keys with String(key).trim() and skip empty results
- If the data is dirty, put it in the header value (encodeURIComponent) and use a constant name
Example fix
// before
const name = `trace-${userTag}`; // userTag = "my tag"
response.setHeader(name, "1"); // ERR_INVALID_HTTP_TOKEN
// after
const name = `trace-${String(userTag).trim().replace(/[^!#$%&'*+\-.^_`|~0-9A-Za-z]/g, "-")}`;
if (name) response.setHeader(name, "1"); Defensive patterns
Strategy: validation
Validate before calling
const TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
function safeHeaderName(name) {
const s = String(name).trim();
return TOKEN_RE.test(s) ? s : null;
}
const safe = safeHeaderName(userSuppliedName);
if (safe) res.setHeader(safe, value); Type guard
function isValidHeaderName(name: unknown): name is string {
return typeof name === "string" && name.length > 0 &&
/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name);
} Try / catch
try {
res.setHeader(name, value);
} catch (err) {
if (err.code === "ERR_INVALID_HTTP_TOKEN") return; // skip bad name, keep serving
throw err;
} Prevention
- Allow-list header names; let user input control only values
- Validate names at the trust boundary (request parsing), not at setHeader time
- Unit-test that every header your service emits matches the token regex
When it happens
Trigger: response.setHeader("", value); setHeader("content type", v) with a space in the name; passing a non-string key (number, symbol from Object.keys of a Map); header names built from user input containing spaces, unicode, or control characters.
Common situations: Proxies/gateways forwarding arbitrary client-supplied header names; names derived from filenames, IDs, or locale strings; header-injection payloads ('x-inject\r\nHost:...') reaching the validator; code ported from http1 stacks that were lenient.
Related errors
- ERR_INVALID_HTTP_TOKEN
- Invalid header value: "${value}"
- ERR_HTTP2_INVALID_HEADER_VALUE
- Invalid header: length must be 2, but is ${header.length}
- Invalid header name: "${name}"
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/4eca35dd687b2234.
Report an issue: GitHub.