nodejs/node · error · TypeError
expected opts.skipHeaderNames to be an array, got ${typeof s
Error message
expected opts.skipHeaderNames to be an array, got ${typeof skipHeaderNames} What it means
Thrown by the deduplicate interceptor when `skipHeaderNames` is destructured from `opts` (default `[]`) but is not an Array. `skipHeaderNames` lists request headers whose presence should bypass deduplication — e.g. an `Authorization` header that changes per caller. The factory validates the shape synchronously before building the lowercase Set used for case-insensitive matching.
Source
Thrown at deps/undici/src/lib/interceptor/deduplicate.js:37
maxBufferSize = 5 * 1024 * 1024
} = opts
if (typeof opts !== 'object' || opts === null) {
throw new TypeError(`expected type of opts to be an Object, got ${opts === null ? 'null' : typeof opts}`)
}
if (!Array.isArray(methods)) {
throw new TypeError(`expected opts.methods to be an array, got ${typeof methods}`)
}
for (const method of methods) {
if (!util.safeHTTPMethods.includes(method)) {
throw new TypeError(`expected opts.methods to only contain safe HTTP methods, got ${method}`)
}
}
if (!Array.isArray(skipHeaderNames)) {
throw new TypeError(`expected opts.skipHeaderNames to be an array, got ${typeof skipHeaderNames}`)
}
if (!Array.isArray(excludeHeaderNames)) {
throw new TypeError(`expected opts.excludeHeaderNames to be an array, got ${typeof excludeHeaderNames}`)
}
if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {
throw new TypeError(`expected opts.maxBufferSize to be a positive finite number, got ${maxBufferSize}`)
}
// Convert to lowercase Set for case-insensitive header matching
const skipHeaderNamesSet = new Set(skipHeaderNames.map(name => name.toLowerCase()))
// Convert to lowercase Set for case-insensitive header exclusion from deduplication key
const excludeHeaderNamesSet = new Set(excludeHeaderNames.map(name => name.toLowerCase()))
/**
* Map of pending requests for deduplicationView on GitHub (pinned to 1b2de5e052)
Solutions
- Pass an array of header-name strings: `skipHeaderNames: ['authorization', 'cookie']`.
- Names are lowercased internally, so any casing works, but each entry must be a string element of an array.
- If sourcing from config, normalize scalars: `const skip = Array.isArray(x) ? x : x ? [x] : []`.
Example fix
// before
deduplicate({ skipHeaderNames: 'authorization' })
// after
deduplicate({ skipHeaderNames: ['authorization'] }) Defensive patterns
Strategy: validation
Validate before calling
function toStringArray(v) {
if (v == null) return []
if (Array.isArray(v)) return v.map(String)
if (typeof v === 'string') return v.split(',').map(s => s.trim()).filter(Boolean)
throw new TypeError('expected string or array of header names')
} Type guard
function isHeaderNameArray(v) {
return Array.isArray(v) && v.every(x => typeof x === 'string')
} Prevention
- Always pass header-name lists as arrays.
- Centralize header-name normalization in one helper.
When it happens
Trigger: Passing `skipHeaderNames: 'authorization'` (a bare string) instead of `['authorization']`; passing a comma-separated string like `'authorization,cookie'`; passing an object map of header overrides. The `Array.isArray(skipHeaderNames)` check fails and the interceptor never builds its skip set.
Common situations: Reusing a header name string from elsewhere in config; treating the option like Node's `http` header object; migrating from an API that accepted a single string. Because the default is `[]`, the error only surfaces when you explicitly opt into header-skipping.
Related errors
- expected opts.excludeHeaderNames to be an array, got ${typeo
- expected opts.methods to be an array, got ${typeof methods}
- expected opts.maxBufferSize to be a positive finite number,
- expected type of opts to be an Object, got ${opts === null ?
- expected opts.methods to only contain safe HTTP methods, got
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/5008d5e0a38f36ae.
Report an issue: GitHub.