undici icon indicating copy to clipboard operation
undici copied to clipboard

`fetch()` in Node.js ignores connection timeout; no way to override 10s default

Open joe-oli opened this issue 11 months ago • 3 comments

Bug Description

The global fetch() implementation in Node.js (since v18) uses Undici under the hood. However, there’s no way to configure connection-level timeouts, specifically the connectTimeout, which defaults to 10 seconds.

Even when using AbortSignal.timeout(ms) to a higher value, say 20000, it does not affect Undici's internal socket connection timeout.

Login error: TypeError: fetch failed at node:internal/deps/undici/undici:13502:13 at processTicksAndRejections (node:internal/process/task_queues:105:5) at async authenticateUser (C:\path\to\myApp\api-app\src\services\keycloak.ts:42:22) at async C:\path\to\myApp\api-app\src\routes\auth.ts:10:23 { [cause]: ConnectTimeoutError: Connect Timeout Error (attempted address: 10.2.2.164:80, timeout: 10000ms) at onConnectTimeout (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:912:19) at Immediate._onImmediate (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:863:35) at processImmediate (node:internal/timers:491:21) { code: 'UND_ERR_CONNECT_TIMEOUT' } }

Reproducible By

The following fetch-based call always fails at 10 seconds, even though both AbortSignal.timeout() and agent were tried.

// Broken: fetch() fails after 10s regardless of signal
const undiciAgent = new Undici.Agent({
  connectTimeout: 20000,
  keepAliveTimeout: 1
});

const response = await fetch('http://10.2.2.164:80/realms/dev/protocol/openid-connect/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({ /* ... */ }),
  signal: AbortSignal.timeout(20000), // Makes no difference
  dispatcher: undiciAgent, // Makes no difference
});

Fails with:

ConnectTimeoutError: Connect Timeout Error (attempted address: 10.2.2.164:80, timeout: 10000ms)

Error in console; NOTE THE PART timeout: 10000ms) Login error: TypeError: fetch failed at node:internal/deps/undici/undici:13502:13 at processTicksAndRejections (node:internal/process/task_queues:105:5) at async authenticateUser (C:\path\to\myApp\api-app\src\services\keycloak.ts:42:22) at async C:\path\to\myApp\api-app\src\routes\auth.ts:10:23 { [cause]: ConnectTimeoutError: Connect Timeout Error (attempted address: 10.2.2.164:80, timeout: 10000ms) at onConnectTimeout (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:912:19) at Immediate._onImmediate (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:863:35) at processImmediate (node:internal/timers:491:21) { code: 'UND_ERR_CONNECT_TIMEOUT' } }

Expected Behavior

The timeout error SHOULD RESPECT WHAT IS SPECIFIED, whether via the Undici.Agent or AbortSignal

[cause]: ConnectTimeoutError: Connect Timeout Error (attempted address: 10.2.2.164:80, timeout: 20000ms)

Logs & Screenshots

console.logs: Login error: TypeError: fetch failed at node:internal/deps/undici/undici:13502:13 at processTicksAndRejections (node:internal/process/task_queues:105:5) at async authenticateUser (C:\path\to\myApp\api-app\src\services\keycloak.ts:42:22) at async C:\path\to\myApp\api-app\src\routes\auth.ts:10:23 { [cause]: ConnectTimeoutError: Connect Timeout Error (attempted address: 10.2.2.164:80, timeout: 10000ms) at onConnectTimeout (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:912:19) at Immediate._onImmediate (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:863:35) at processImmediate (node:internal/timers:491:21) { code: 'UND_ERR_CONNECT_TIMEOUT' } }

Environment

Node.js: 22.1.0 Undici: ^6.15.0 ?? (whatever come with Node 22.x) OS: Windows 11

Additional context

Using undici.request() directly does respect connectTimeout and works perfectly:

import { request, Agent as UndiciAgent } from 'undici';

const undiciAgent = new UndiciAgent({
  connectTimeout: 20000, // Works as expected
  keepAliveTimeout: 1
});

const { statusCode, body } = await request('http://10.2.2.164:80/realms/dev/protocol/openid-connect/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({ /* ... */ }).toString(),
  dispatcher: undiciAgent, // This is honored
});

console.logs: Login error: ConnectTimeoutError: Connect Timeout Error (attempted address: 10.2.2.164:80, timeout: 20000ms) at onConnectTimeout (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:912:19) at Immediate._onImmediate (C:\path\to\myApp\api-app\node_modules\undici\lib\core\util.js:863:35) at processImmediate (node:internal/timers:491:21) { code: 'UND_ERR_CONNECT_TIMEOUT' }

joe-oli avatar May 15 '25 07:05 joe-oli

Thanks for reporting!

Can you provide steps to reproduce? We often need a reproducible example, e.g. some code that allows someone else to recreate your problem by just copying and pasting it. If it involves more than a couple of different file, create a new repository on GitHub and add a link to that. (this is missing a server)

mcollina avatar May 15 '25 07:05 mcollina

Thanks for reporting!

Can you provide steps to reproduce? We often need a reproducible example, e.g. some code that allows someone else to recreate your problem by just copying and pasting it. If it involves more than a couple of different file, create a new repository on GitHub and add a link to that. (this is missing a server)

I came across this issue because getting to my server ( 10.2.2.164, within a corporate network) was not stable at the networking level. It took a while for a ping to respond - intermittently issue, sometimes it would respond fast under 10 seconds, sometimes slow more than 10 seconds;

If it took longer than 10 seconds to establish a TCP connection, the 'fetch' would abort at exactly 10 seconds. Which is the problem with the fetch library built into node js, It does not respect my attempts to modify the connection timeout.

As for reproducible code:

  • The client is a simple fetch call with standard options (e.g., Agent, headers, body).
  • The server doesn’t need specific behavior (e.g., delayed responses post-connection); it’s just an endpoint with a slow TCP connection.
  • Response Timeouts: The issue is not about slow server responses after connection, but the TCP connection phase itself.

Hence the challenge. I will try to simulate a slow TCP connection using both node.js client and node,js server code, but it will take time. (If I do, I will come back to update the issue)

KEY ISSUES:

  • Trigger the UND_ERR_CONNECT_TIMEOUT error at ~10 seconds with the node:internal/deps/undici/undici:2602:28 stack trace.
  • Confirm that connect: { timeout: 20000 } is ignored, failing earlier than 20 seconds.

joe-oli avatar May 15 '25 08:05 joe-oli

Without an Minimum Reproducible Example is a bit hard to assess.

metcoder95 avatar May 15 '25 16:05 metcoder95

Seems like duplicate of #4405. Closing in favor of #4405.

Uzlopak avatar Aug 13 '25 01:08 Uzlopak