Skip to content

Pool and RoundRobinPool can hang requests when clientTtl is used together with a connections limit. #5144

@trivikr

Description

@trivikr

Bug Description

Pool and RoundRobinPool can hang requests when clientTtl is used together with a connections limit.

Reproducible By

import http from "node:http";
import { once } from "node:events";
import { setTimeout as delay } from "node:timers/promises";
import { Pool } from "undici";

const server = http.createServer((req, res) => res.end("ok"));

server.listen(0);
await once(server, "listening");

const pool = new Pool(`http://localhost:${server.address().port}`, {
  connections: 1,
  clientTtl: 1,
});
const request = () =>
  pool.request({ path: "/", method: "GET" }).then(({ body }) => body.text());

try {
  await request();
  await delay(20);
  await Promise.race([
    request(),
    delay(1000).then(() => {
      throw new Error("second request hung");
    }),
  ]);
} catch (err) {
  console.error(err.message);
  process.exitCode = 1;
} finally {
  pool.destroy();
  server.close();
}

Expected Behavior

When a stale client is removed because its TTL expired, the pool should be able to create a replacement client immediately when under the configured usable connection limit.

Logs & Screenshots

The second request times out

second request hung

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