nodejs/node · error · TypeError

expected ${name} to be an array or undefined, got ${typeof o

Error message

expected ${name} to be an array or undefined, got ${typeof origins}

What it means

Thrown by assertCacheOrigins in the cache() interceptor factory. The origins option (a whitelist of origins eligible for caching) must be undefined (cache all origins) or an array of strings/RegExps. Passing any other type (a single string, an object, a number, a Set) is rejected.

Source

Thrown at deps/undici/src/lib/interceptor/cache.js:20

const assert = require('node:assert')
const { Readable } = require('node:stream')
const util = require('../core/util')
const CacheHandler = require('../handler/cache-handler')
const MemoryCacheStore = require('../cache/memory-cache-store')
const CacheRevalidationHandler = require('../handler/cache-revalidation-handler')
const { assertCacheStore, assertCacheMethods, makeCacheKey, normalizeHeaders, parseCacheControlHeader, isInvalidOrWildcardVaryHeader } = require('../util/cache.js')
const { AbortError } = require('../core/errors.js')
const { parseHttpDate } = require('../util/date.js')

/**
 * @param {(string | RegExp)[] | undefined} origins
 * @param {string} name
 */
function assertCacheOrigins (origins, name) {
  if (origins === undefined) return
  if (!Array.isArray(origins)) {
    throw new TypeError(`expected ${name} to be an array or undefined, got ${typeof origins}`)
  }
  for (let i = 0; i < origins.length; i++) {
    const origin = origins[i]
    if (typeof origin !== 'string' && !(origin instanceof RegExp)) {
      throw new TypeError(`expected ${name}[${i}] to be a string or RegExp, got ${typeof origin}`)
    }
  }
}

const nop = () => {}

function trimOWS (value) {
  return value.replace(/^[\t ]+|[\t ]+$/g, '')
}

function arrayIncludes (array, value) {
  for (let i = 0; i < array.length; i++) {
    if (array[i] === value) {

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Pass origins as an array: ['https://a.com', /https:\/\/.*\.internal/], or omit it to cache all origins.
  2. Convert single strings: ['https://a.com']; convert Sets: Array.from(set).
  3. Validate the option at the config boundary before composing the interceptor.

Example fix

// before
client.compose(interceptors.cache({ origins: 'https://api.example.com' }))

// after
client.compose(interceptors.cache({ origins: ['https://api.example.com'] }))
Defensive patterns

Strategy: validation

Validate before calling

function normalizeOrigins(v) {
  if (v == null) return undefined
  return Array.isArray(v) ? v : [v]
}

Type guard

function isOrigins(v) { return v == null || (Array.isArray(v) && v.every(o => typeof o === 'string' || o instanceof RegExp)) }

Try / catch

try { client.compose(interceptors.cache({ origins })) } catch (e) { if (e instanceof TypeError && /to be an array/.test(e.message)) { client.compose(interceptors.cache({ origins: Array.isArray(origins) ? origins : [origins] })) } else throw e }

Prevention

When it happens

Trigger: Calling interceptors.cache({ origins: ... }) with origins as a string ('https://a.com'), a Set, an object map, or any non-array value.

Common situations: Configuring the cache interceptor from a config file that used a single string or a Set; copy-pasting an example that used a different shape; TypeScript where the type was widened.

Related errors


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