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' }
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 theconnectTimeout, 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()andagentwere tried.Fails with:
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 respectconnectTimeoutand works perfectly: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' }