Skip to content

MockAgent: fetch fails with "Request was cancelled" when following a redirect #5963

Open

Description

@santiago-su

Bug Description

Reproduction

Since 8.11.1, fetch with a MockAgent dispatcher fails on any redirect that is followed. The follow-up request is cancelled and fetch rejects with TypeError: fetch failed, caused by Request was cancelled..

Redirects against a real server still work. Only MockAgent is affected.

The regression comes from #5850 (8145595e, "fix hang on 3xx response with large body"). That change makes onResponseError in lib/web/fetch/index.js skip fetchParams.controller.terminate(error) when controller.aborted is true:

if (!controller?.aborted) {
  fetchParams.controller.terminate(error)
}

The real Request exposes get aborted() (lib/core/request.js). The controller that dispatchMockReply builds in lib/mock/mock-utils.js has no aborted property. During a redirect, fetch aborts the previous connection. With MockAgent the guard never sees the abort, so the fetch is terminated and the follow-up request fails.

Standalone reproduction script:

'use strict'

const { test } = require('node:test')
const { MockAgent, fetch } = require('undici')

test('MockAgent: fetch follows a redirect', async (t) => {
  t.plan(3)

  const mockAgent = new MockAgent()
  mockAgent.disableNetConnect()
  t.after(() => mockAgent.close())

  const mockPool = mockAgent.get('http://localhost')
  mockPool.intercept({ path: '/redirect', method: 'GET' }).reply(307, '', { headers: { location: 'http://localhost/final' } })
  mockPool.intercept({ path: '/final', method: 'GET' }).reply(200, 'ok')

  const response = await fetch('http://localhost/redirect', { dispatcher: mockAgent })

  t.assert.strictEqual(response.status, 200)
  t.assert.strictEqual(await response.text(), 'ok')
  t.assert.ok(response.redirected)
})

Run with node --test repro.js.

Expected Behavior

fetch follows the 307 and resolves with status 200, body ok and response.redirected === true. This is what 8.11.0 does.

Actual Behavior

fetch rejects with TypeError: fetch failed. The cause is Request was cancelled., created in makeAppropriateNetworkError (lib/web/fetch/response.js) and returned by schemeFetch (lib/web/fetch/index.js) for the request made after the redirect.

undici result
8.10.0, 8.10.2, 8.11.0 passes
8.11.1, 8.11.2 fails

Against a real http.createServer that answers /a with a 307 to /b, 8.11.2 follows the redirect and returns 200.

Logs & Screenshots

N/A

Environment

  • OS: macOS
  • Node.js version: v24.12.0
  • undici version: 8.11.1 and 8.11.2 (8.11.0 is not affected)

Additional context

Proposed fix: give the mock controller an aborted getter in lib/mock/mock-utils.js:

const controller = {
  paused: false,
  get aborted () {
    return aborted
  },
  ...

With this change the script above passes, and the existing mock and fetch suites pass. I've opened a PR #5964 with this change and a regression test in test/mock-agent.js.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions