nodejs/node · error · MaxOriginsReachedError

UND_ERR_MAX_ORIGINS_REACHED

UND_ERR_MAX_ORIGINS_REACHED

Error message

Maximum allowed origins reached

What it means

Thrown inside Agent's [kDispatch] as a MaxOriginsReachedError (code UND_ERR_MAX_ORIGINS_REACHED). The check is: this[kOrigins].size >= this[kOptions].maxOrigins AND the current origin is not already tracked. The Agent refuses to silently evict or grow beyond the configured cap, so a brand-new origin past the limit is rejected outright.

Source

Thrown at deps/undici/src/lib/dispatcher/agent.js:86

    for (const dispatcher of this[kClients].values()) {
      ret += dispatcher[kRunning]
    }
    return ret
  }

  [kDispatch] (opts, handler) {
    let origin
    if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) {
      origin = String(opts.origin)
    } else {
      throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.')
    }

    const allowH2 = opts.allowH2 ?? this[kOptions].allowH2
    const key = allowH2 === false ? `${origin}#http1-only` : origin

    if (this[kOrigins].size >= this[kOptions].maxOrigins && !this[kOrigins].has(origin)) {
      throw new MaxOriginsReachedError()
    }

    let dispatcher = this[kClients].get(key)
    if (!dispatcher) {
      dispatcher = this[kFactory](opts.origin, allowH2 === false
        ? { ...this[kOptions], allowH2: false }
        : this[kOptions])

      const closeClientIfUnused = () => {
        if (this[kClients].get(key) !== dispatcher) {
          return
        }

        if (dispatcher[kConnected] > 0 || dispatcher[kBusy]) {
          return
        }

        this[kClients].delete(key)

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Raise maxOrigins to cover the expected origin cardinality (or omit it for Infinity).
  2. Pool repeated origins so they count once — make sure the origin string is normalized (trailing slash, lowercase host).
  3. Catch MaxOriginsReachedError and fall back to a fresh Client/Pool for overflow origins.

Example fix

// before
const agent = new Agent({ maxOrigins: 2 })
await agent.request({ origin: 'https://a.com', method: 'GET', path: '/' })
await agent.request({ origin: 'https://b.com', method: 'GET', path: '/' })
await agent.request({ origin: 'https://c.com', method: 'GET', path: '/' }) // throws
// after
const agent = new Agent({ maxOrigins: Infinity })
Defensive patterns

Strategy: fallback

Validate before calling

function boundedDispatch(agent, opts, handler, overflowPool) {
  try {
    return agent.dispatch(opts, handler)
  } catch (e) {
    if (e.code === 'UND_ERR_MAX_ORIGINS_REACHED') return overflowPool.dispatch(opts, handler)
    throw e
  }
}

Type guard

const isMaxOriginsError = (e) => e?.code === 'UND_ERR_MAX_ORIGINS_REACHED'

Try / catch

try {
  await agent.request({ origin, method, path })
} catch (e) {
  if (e.code === 'UND_ERR_MAX_ORIGINS_REACHED') {
    // raise maxOrigins or route via a fallback Client
  } else throw e
}

Prevention

When it happens

Trigger: new Agent({ maxOrigins: 3 }) then dispatching to four DISTINCT origins (the fourth, never-seen-before origin triggers the throw); a crawler hitting many hosts through a capped Agent.

Common situations: A polyglot client pointed at many microservices through one low-capped Agent; raising traffic to include more origins than the configured cap; mis-tuning maxOrigins down for memory and then seeing new hosts fail.

Related errors


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