denoland/deno · error · NodeTypeError
ERR_INVALID_ARG_VALUE
ERR_INVALID_ARG_VALUE
Error message
The argument 'headers' is invalid. Received ${inspect(obj)} What it means
In writeHead's slow path - taken when setHeader/appendHeader were already used (this[kOutHeaders] is set) - an array argument must be a flat [name1, value1, name2, value2, ...] list. An odd-length array leaves a name without a value, so the polyfill throws ERR_INVALID_ARG_VALUE('headers', obj) exactly like Node.
Source
Thrown at ext/node/polyfills/_http_server.js:534
if (typeof reason === "string") {
this.statusMessage = reason;
} else {
this.statusMessage ||= STATUS_CODES[statusCode] || "unknown";
obj ??= reason;
}
this.statusCode = statusCode;
// Enforce no body for 204 and 304 responses
if (statusCode === 204 || statusCode === 304) {
this._hasBody = false;
}
let headers;
if (this[kOutHeaders]) {
// Slow-case: progressive API and header fields are passed.
if (ArrayIsArray(obj)) {
if (obj.length % 2 !== 0) {
throw new ERR_INVALID_ARG_VALUE("headers", obj);
}
for (let n = 0; n < obj.length; n += 2) {
const k = obj[n + 0];
if (k) this.removeHeader(k);
}
for (let n = 0; n < obj.length; n += 2) {
const k = obj[n + 0];
if (k) this.appendHeader(k, obj[n + 1]);
}
} else if (obj) {
const keys = ObjectKeys(obj);
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
if (k) this.setHeader(k, obj[k]);
}
}
headers = this[kOutHeaders];
} else {View on GitHub (pinned to 89f33cbef2)
Solutions
- Fix the array to even length with strict name/value pairs
- Prefer the object form writeHead(200, { 'content-type': 'text/html' }) which cannot be odd
- When the array is dynamic, assert headers.length % 2 === 0 before calling
- Centralize header construction in one tested helper
Example fix
// before
const h = ['content-type', 'text/html'];
if (cors) h.push('access-control-allow-origin'); // odd length -> throws
res.writeHead(200, h);
// after
const h = { 'content-type': 'text/html' };
if (cors) h['access-control-allow-origin'] = '*';
res.writeHead(200, h); Defensive patterns
Strategy: validation
Validate before calling
function validHeaderPairs(headers) {
if (!Array.isArray(headers)) return true;
return headers.length % 2 === 0;
}
if (validHeaderPairs(pairs)) {
res.writeHead(200, reason, pairs);
} Type guard
function isHeaderPairArray(v) {
return !Array.isArray(v) || v.length % 2 === 0;
} Try / catch
try {
res.writeHead(200, reason, pairs);
} catch (e) {
if (e.code === 'ERR_INVALID_ARG_VALUE') {
res.writeHead(200, reason); // fall back to headers set via setHeader
} else throw e;
} Prevention
- Prefer the object headers form whenever pairs are built dynamically
- Push header names and values together in a single helper call
- Assert even length in the header-builder's unit tests
When it happens
Trigger: Mixing the progressive API (res.setHeader earlier) with writeHead(200, reason, ['Content-Type', 'text/html', 'X-Extra']) whose array has odd length; dynamically built pairs arrays where a key is pushed without its value.
Common situations: Conditionally appending header pairs with array.push(name) while forgetting the value; spreading a computed array that can drop entries; header builders whose schema drifted so a value can be undefined and skipped.
Related errors
- ERR_INVALID_ARG_TYPE
- ERR_INVALID_URL
- ERR_UNESCAPED_CHARACTERS
- ERR_INVALID_HTTP_TOKEN
- ERR_INVALID_HTTP_TOKEN
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/82a6f730cc261cda.
Report an issue: GitHub.