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 deduplication

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Pass an array of header-name strings: `skipHeaderNames: ['authorization', 'cookie']`.
  2. Names are lowercased internally, so any casing works, but each entry must be a string element of an array.
  3. 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

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


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/5008d5e0a38f36ae. Report an issue: GitHub.