Skip to content

Pool.dispatch() can return false and report backpressure even though another client is available #5150

@trivikr

Description

@trivikr

Bug Description

Pool.dispatch() can return false and report backpressure even though another client is available

Reproducible By

import { EventEmitter } from 'node:events'
import { setTimeout as sleep } from 'node:timers/promises'
import { Pool } from 'undici'

let n = 0

class Client extends EventEmitter {
  id = ++n
  closed = false
  destroyed = false

  dispatch () {
    if (this.id === 1) {
      this.emit('connect', new URL('http://example.com'), [this])
      return false
    }

    return true
  }

  close (cb) {
    cb?.()
  }
}

const handler = {
  onConnect () {},
  onHeaders () {},
  onData () {},
  onComplete () {},
  onError () {},
  onResponseError () {}
}

const pool = new Pool('http://example.com', {
  connections: 2,
  clientTtl: 1,
  factory: () => new Client()
})

// Creates client #1 which returns false, so pool marks it as draining.
// pool-level backpressure state is true. The pool calls kGetDispatcher() again and creates client #2.
console.log('first dispatch: ',  pool.dispatch({ path: '/', method: 'GET' }, handler))

// Wait for clientTtl to expire, after which client #1 is removed.
await sleep(5)

// The second dispatch goes to client #2, whose dispatch() returns true.
// pool-level backpressure state is false, though.
console.log('second dispatch: ',  pool.dispatch({ path: '/', method: 'GET' }, handler))

Expected Behavior

Pool.dispatch() should return true when a remaining client is available and accepts the request.

Removing one stale client should not make the pool report backpressure while another non-draining, open client exists.

Logs & Screenshots

first dispatch:  true
second dispatch:  false

Environment

macOS 26.4.1
Node v24.15.0
undici v8.1.0

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions