nodejs/node · error · InvalidArgumentError

Redirect loop detected. Cannot redirect to ${origin}. This t

Error message

Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.

What it means

Thrown as an InvalidArgumentError from RedirectHandler.onResponseStart when a URL about to be followed already exists in this.history, i.e. the handler is about to revisit a URL it has already been to. Undici's Client and Pool are bound to a single origin and cannot themselves perform cross-origin redirects, so a cross-origin redirect bounces back to the same URL forever; this guard turns that infinite loop into a single explicit error and points you at the fix (use an Agent).

Source

Thrown at deps/undici/src/lib/handler/redirect-handler.js:102

    if (this.opts.origin) {
      this.history.push(new URL(this.opts.path, this.opts.origin))
    }

    if (!this.location) {
      this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)
      return
    }

    const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)))
    const path = search ? `${pathname}${search}` : pathname

    // Check for redirect loops by seeing if we've already visited this URL in our history
    // This catches the case where Client/Pool try to handle cross-origin redirects but fail
    // and keep redirecting to the same URL in an infinite loop
    const redirectUrlString = `${origin}${path}`
    for (const historyUrl of this.history) {
      if (historyUrl.toString() === redirectUrlString) {
        throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`)
      }
    }

    // Remove headers referring to the original URL.
    // By default it is Host only. A 303 or a 301/302 POST-to-GET redirect also removes all Content-* headers.
    // https://tools.ietf.org/html/rfc7231#section-6.4
    this.opts.headers = cleanRequestHeaders(this.opts.headers, removeContentHeaders, this.opts.origin !== origin, this.stripHeadersOnRedirect, this.stripHeadersOnCrossOriginRedirect)
    this.opts.path = path
    this.opts.origin = origin
    this.opts.query = null
  }

  onResponseData (controller, chunk) {
    if (this.location) {
      /*
        https://tools.ietf.org/html/rfc7231#section-6.4

        TLDR: undici always ignores 3xx response bodies.

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Use an Agent instead of a Client/Pool; Agent can resolve and dispatch to the new origin on each redirect.
  2. If you must use a Client/Pool, disable redirect following (maxRedirections: 0) and resolve cross-origin Locations manually.
  3. Check the redirect target's origin; if it differs from the client origin, issue a fresh request to a properly scoped dispatcher.

Example fix

// before
const client = new Client('https://api.example.com')
await client.request({ path: '/me', maxRedirections: 5 }) // 3xx -> https://auth.example.com

// after
const agent = new Agent() // can follow cross-origin redirects
await agent.request({ origin: 'https://api.example.com', path: '/me', maxRedirections: 5 })
Defensive patterns

Strategy: validation

Validate before calling

function pickDispatcher(targetOrigin, agent, client) {
  // Client/Pool are single-origin; cross-origin redirects need an Agent
  return client.origin === targetOrigin ? client : agent
}

Try / catch

try { await client.request({ path, maxRedirections: 5 }) } catch (e) { if (e.code === 'UND_ERR_INVALID_ARG' && /Redirect loop detected/.test(e.message)) { await agent.request({ origin: targetOrigin, path, maxRedirections: 5 }) } else throw e }

Prevention

When it happens

Trigger: Using a Client or Pool (single-origin) against a server that issues a cross-origin 3xx redirect that the server then redirects back from, creating an A->B->A cycle. The handler detects the repeat URL and aborts.

Common situations: Pointing a Pool/Client at a load balancer or CDN that redirects to a different host; SSO/OAuth flows that hop domains; testing against httpbin-like services that echo redirects across origins.

Related errors


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