nodejs/node · error · UndiciError

UND_ERR

UND_ERR

Error message

1 interceptor is pending:\n\n${pendingInterceptorsFormatter.format(pending)}

What it means

Thrown by `MockAgent.assertNoPendingInterceptors()` (an `UndiciError`, generic code `UND_ERR`) when exactly one mocked interceptor was registered but never matched/consumed by a real dispatch. In test suites this assertion verifies that every mocked route was actually exercised — a pending interceptor usually means a typo in the path/method, a wrong origin, or dead test setup. The message includes a formatted description of the unmatched interceptor.

Source

Thrown at deps/undici/src/lib/mock/mock-agent.js:224

    return this[kNetConnect]
  }

  pendingInterceptors () {
    const mockAgentClients = this[kClients]

    return Array.from(mockAgentClients.entries())
      .flatMap(([origin, dispatcher]) => dispatcher[kDispatches].map(dispatch => ({ ...dispatch, origin })))
      .filter(({ pending }) => pending)
  }

  assertNoPendingInterceptors ({ pendingInterceptorsFormatter = new PendingInterceptorsFormatter() } = {}) {
    const pending = this.pendingInterceptors()

    if (pending.length === 0) {
      return
    }

    throw new UndiciError(
      pending.length === 1
        ? `1 interceptor is pending:\n\n${pendingInterceptorsFormatter.format(pending)}`.trim()
        : `${pending.length} interceptors are pending:\n\n${pendingInterceptorsFormatter.format(pending)}`.trim()
    )
  }
}

module.exports = MockAgent

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Compare the intercept's `path`/`method`/`origin` against the actual request your code makes — use `mockAgent.getCallHistory()` if call history is enabled.
  2. Ensure the code under test actually runs and reaches the mocked dispatch (no early return, no swallowed error).
  3. If the interceptor is intentionally unused, remove it or split it into its own test.
  4. Check for trailing-slash and protocol/port mismatches; consider enabling `ignoreTrailingSlash` on the MockAgent.

Example fix

// before
mockAgent.get('https://api.test').intercept({ path: '/user', method: 'GET' }).reply(200)
await fetch('https://api.test/users') // typo: /user vs /users
mockAgent.assertNoPendingInterceptors() // throws: 1 pending

// after
mockAgent.get('https://api.test').intercept({ path: '/users', method: 'GET' }).reply(200)
await fetch('https://api.test/users')
mockAgent.assertNoPendingInterceptors() // passes
Defensive patterns

Strategy: validation

Validate before calling

function assertAllInterceptorsConsumed(mockAgent) {
  const pending = mockAgent.pendingInterceptors()
  if (pending.length === 1) {
    const p = pending[0]
    throw new Error(`Unmatched intercept: ${p.method} ${p.origin}${p.path}`)
  }
}

Try / catch

try {
  mockAgent.assertNoPendingInterceptors()
} catch (err) {
  // err lists the unmatched interceptor; reconcile path/method/origin
  throw err
}

Prevention

When it happens

Trigger: Registering `mockAgent.get(origin).intercept({ path: '/users', method: 'GET' }).reply(200)` but the code under test calls `/user` (typo), a different origin, or `POST`. After the test, `assertNoPendingInterceptors()` finds the one unmatched entry and throws. Singular form is chosen because `pending.length === 1`.

Common situations: Path/method typos between the intercept declaration and the code under test; origin mismatch (trailing slash, protocol, port); the code under test not being invoked (skipped branch, early throw); intercept declared on the wrong MockClient/MockPool; query-string differences when matching is strict.

Related errors


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