nodejs/node · error · TypeError
expected opts.methods to be an array, got ${typeof methods}
Error message
expected opts.methods to be an array, got ${typeof methods} What it means
Thrown by the deduplicate interceptor's factory function when the `methods` option is destructured from `opts` but is not an Array. The deduplicate interceptor merges concurrent identical safe-method requests into a single network call, so it must know which HTTP methods are eligible. Because the factory destructures `methods` with a default of `['GET']`, this fires only when you explicitly pass a non-array value. It is a synchronous TypeError raised at interceptor construction time (when you call `createDeduplicateInterceptor(opts)` or compose it), not per-request.
Source
Thrown at deps/undici/src/lib/interceptor/deduplicate.js:27
/**
* @param {import('../../types/interceptors.d.ts').default.DeduplicateInterceptorOpts} [opts]
* @returns {import('../../types/dispatcher.d.ts').default.DispatcherComposeInterceptor}
*/
module.exports = (opts = {}) => {
const {
methods = ['GET'],
skipHeaderNames = [],
excludeHeaderNames = [],
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}`)View on GitHub (pinned to 1b2de5e052)
Solutions
- Change the `methods` option to an array of HTTP method strings, e.g. `methods: ['GET']` or `methods: ['GET', 'HEAD']`.
- If loading options from external config, coerce with `Array.isArray(opts.methods) ? opts.methods : [opts.methods]` before passing them in.
- If you meant to deduplicate only one method, pass a one-element array rather than a bare string.
- Audit the surrounding config object for other shape mismatches (skipHeaderNames, excludeHeaderNames, maxBufferSize) since they validate in the same block.
Example fix
// before
const agent = new Agent().compose([deduplicate({ methods: 'GET' })])
// after
const agent = new Agent().compose([deduplicate({ methods: ['GET'] })]) Defensive patterns
Strategy: validation
Validate before calling
function validateDeduplicateOpts(opts) {
if (!Array.isArray(opts.methods)) {
opts.methods = Array.isArray(opts.methods) ? opts.methods : [opts.methods].filter(Boolean)
}
if (!Array.isArray(opts.methods)) {
throw new TypeError('opts.methods must be an array of safe HTTP methods')
}
return opts
} Type guard
function isMethodArray(v) {
return Array.isArray(v) && v.every(m => typeof m === 'string')
} Prevention
- Always pass `methods` as an array even for a single method.
- Load interceptor config through a shared validator in your bootstrap code.
- Enable TypeScript strict types from undici's interceptor definitions.
When it happens
Trigger: Calling the deduplicate interceptor factory with `opts.methods` set to a string (e.g. `'GET'`), a number, a plain object, `true`, or any other non-array. Passing `methods: 'GET'` instead of `methods: ['GET']` is the canonical trigger. The check `Array.isArray(methods)` fails before any method-content validation runs.
Common situations: Copying a single method from a config file as a bare string; migrating from a single-method API to the array form; reading `methods` out of JSON where it was serialized as a scalar; TypeScript users bypassing types with `as any`. The default `['GET']` masks the requirement, so developers first encounter it only when they try to add `HEAD`.
Related errors
- expected opts.methods to only contain safe HTTP methods, got
- expected opts.skipHeaderNames to be an array, got ${typeof s
- expected opts.excludeHeaderNames to be an array, got ${typeo
- expected opts.maxBufferSize to be a positive finite number,
- expected type of opts to be an Object, got ${opts === null ?
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/59e7f445a560d57b.
Report an issue: GitHub.