denoland/deno · error · NodeTypeError
ERR_HTTP2_HEADER_SINGLE_VALUE
ERR_HTTP2_HEADER_SINGLE_VALUE
Error message
Header field "${key}" must only have a single value What it means
Some HTTP/2 header fields are single-valued by spec (kSingleValueHeaders, util.ts:131 — the pseudo-headers plus content-length, authorization, age, content-encoding/language/location/length, etc.). If such a key arrives with an ARRAY value of length > 1, buildNgHeaderString throws ERR_HTTP2_HEADER_SINGLE_VALUE (util.ts:843) — the value cannot be joined or repeated.
Source
Thrown at ext/node/polyfills/internal/http2/util.ts:843
(v) => StringPrototypeToLowerCase(v),
);
function processHeader(key, value) {
key = StringPrototypeToLowerCase(key);
const isSingleValueHeader = strictSingleValueFields &&
kSingleValueHeaders.has(key);
let isArray = ArrayIsArray(value);
if (isArray) {
switch (value.length) {
case 0:
return;
case 1:
value = String(value[0]);
isArray = false;
break;
default:
if (isSingleValueHeader) {
throw new ERR_HTTP2_HEADER_SINGLE_VALUE(key);
}
}
} else {
value = String(value);
}
if (isSingleValueHeader) {
if (singles.has(key)) {
throw new ERR_HTTP2_HEADER_SINGLE_VALUE(key);
}
singles.add(key);
}
const flags = ArrayPrototypeIncludes(neverIndex, key)
? kNeverIndexFlag
: kNoHeaderFlags;
if (key[0] === ":") {
const err = assertValuePseudoHeader(key);
if (err !== undefined) {
throw err;View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Ensure single-value headers always end up as one scalar string before calling request()/respond(): collapse arrays of length 1 and reject/resolve longer ones.
- If two values exist, decide which one is authoritative (e.g. take the first, or fail with your own error) instead of passing both.
- Keep a deny-list check for kSingleValueHeaders-style fields when merging header objects.
Example fix
// before
stream.respond({ ':status': 200, 'content-length': ['10', '20'] });
// after
stream.respond({ ':status': 200, 'content-length': '10' }); Defensive patterns
Strategy: validation
Validate before calling
const SINGLE = new Set([':status', ':method', ':authority', ':scheme', ':path', ':protocol', 'content-length', 'authorization', 'age', 'content-encoding', 'content-language', 'content-location']);
for (const [k, v] of Object.entries(headers)) {
if (SINGLE.has(k.toLowerCase()) && Array.isArray(v)) {
if (v.length > 1) throw new Error(`${k} must be single-valued`);
headers[k] = v[0];
}
} Type guard
const isSingleValueHeader = (k: string) => SINGLE.has(k.toLowerCase());
Prevention
- Only set-cookie/via style fields may repeat
- Collapse length-1 arrays to scalars before request()/respond()
When it happens
Trigger: Passing headers like { 'content-length': ['42','43'] } or an array pair ['authorization', ['a','b']] to http2session.request()/stream.respond(); arrays of length 0 are skipped and length 1 is unwrapped, only length >= 2 with a single-value key throws here.
Common situations: Merging header sets where one side already had an array (set-cookie is multi-valued and fine, but people over-generalize); query-string parsers producing arrays for repeated params reused as headers; middleware that normalizes every header value into [value].
Related errors
- ERR_INVALID_ARG_VALUE
- ERR_HTTP2_INVALID_PSEUDOHEADER
- ERR_HTTP2_CONNECT_AUTHORITY
- ERR_INVALID_HTTP_TOKEN
- ERR_HTTP2_INVALID_CONNECTION_HEADERS
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/11f4a4a7c14ae1e0.
Report an issue: GitHub.