denoland/deno · error · NodeTypeError
ERR_INVALID_ARG_TYPE
ERR_INVALID_ARG_TYPE
Error message
The "headers" argument must be an instance of Headers or Map. Received ${headers} What it means
setHeaders() duck-types its argument: it must be non-null, not an array, and expose callable `.keys()` and `.get()` - in practice a Headers or Map instance. Anything else (plain object, array of pairs, string) throws ERR_INVALID_ARG_TYPE.
Source
Thrown at ext/node/polyfills/_http_outgoing.ts:1159
},
writable: true,
enumerable: true,
configurable: true,
},
setHeaders: {
__proto__: null,
value: function setHeaders(headers: any) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT("set");
}
if (
!headers ||
ArrayIsArray(headers) ||
typeof headers.keys !== "function" ||
typeof headers.get !== "function"
) {
throw new ERR_INVALID_ARG_TYPE(
"headers",
["Headers", "Map"],
headers,
);
}
let cookies = null;
const iterator = headers[SymbolIterator]();
while (true) {
// deno-lint-ignore deno-internal/prefer-primordials
const { done, value: entry } = iterator.next();
if (done) {
break;
}
const key = entry[0];
const value = entry[1];
if (key === "set-cookie") {
if (ArrayIsArray(value)) {View on GitHub (pinned to 89f33cbef2)
Solutions
- Wrap entry pairs: res.setHeaders(new Map([['x-a','1']]))
- Or wrap an object: res.setHeaders(new Headers({'x-a':'1'}))
- Type the parameter as `Headers | Map<string,string>` in your own wrappers so the mistake is caught at compile time
Example fix
// before
res.setHeaders({ 'x-a': '1', 'x-b': '2' }); // throws ERR_INVALID_ARG_TYPE
// after
res.setHeaders(new Map([['x-a','1'], ['x-b','2']]));
// or
res.setHeaders(new Headers({ 'x-a': '1', 'x-b': '2' })); Defensive patterns
Strategy: type-guard
Validate before calling
const ok = h !== null &&
!Array.isArray(h) &&
typeof h === 'object' &&
typeof h.keys === 'function' &&
typeof h.get === 'function';
if (ok) res.setHeaders(h);
else res.setHeaders(new Map(Object.entries(h ?? {}))); Type guard
function isHeadersLike(v: unknown): v is Headers | Map<string, string> {
return !!v && typeof v === 'object' && !Array.isArray(v) &&
typeof (v as any).keys === 'function' && typeof (v as any).get === 'function';
} Try / catch
try {
res.setHeaders(headers);
} catch (e) {
if (e?.code === 'ERR_INVALID_ARG_TYPE' && /headers/.test(e.message)) {
res.setHeaders(new Map(Object.entries(headers)));
} else throw e;
} Prevention
- Type your helpers as (headers: Headers | Map<string,string>) => void
- Remember setHeaders takes Map/Headers, while setHeader takes name/value strings
- Convert plain objects at the boundary: new Map(Object.entries(obj))
When it happens
Trigger: res.setHeaders({'x-a': '1'}) (plain object), res.setHeaders([['x-a','1']]) (array of entries), res.setHeaders('x-a=1') (string), or res.setHeaders(null).
Common situations: Developers assuming object-literal semantics from setHeader(); passing fetch-style Headers built wrong; passing a serialized headers array from another layer of the app.
Related errors
- ERR_HTTP_HEADERS_SENT
- ERR_HTTP_HEADERS_SENT
- Invalid header name: "${name}"
- Invalid header value: "${value}"
- Unsupported 'alpnProtocols' option provided. 'h2' and 'http/
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/ca2f9fb25cad8312.
Report an issue: GitHub.