Bug Description
When making requests that attempt connection to multiple ip addresses (like a ipv4 and a ipv6), then rejection is quite volatile:
- when last attempted socket ended with timeout then it always throws
ConnectTimeoutError, hiding reasons of previous unsuccessfull connection attempts
- otherwise a AggregateError is throws with raw errors from
node:net inside
Reproducible By
setup firewall
sudo iptables -A INPUT -p tcp --dport 3112 -j DROP
sudo ip6tables -A INPUT -p tcp --dport 3112 -j REJECT --reject-with tcp-reset
sudo iptables -A INPUT -p tcp --dport 3113 -j REJECT --reject-with tcp-reset
sudo ip6tables -A INPUT -p tcp --dport 3113 -j DROP
setup address that resolves to localhost on both families: write to /etc/hosts:
::1 example.com
127.0.0.1 example.com
run in node
await require('undici').request('http://foofoo.in:3112')
await require('undici').request('http://foofoo.in:3113')
Expected Behavior
I would expect both requests to reject similar payload
Logs & Screenshots
> await require('undici').request('http://example.com:3112')
Uncaught:
ConnectTimeoutError: Connect Timeout Error (attempted addresses: ::1:3112, 127.0.0.1:3112, timeout: 10000ms)
at onConnectTimeout (/home/konrad/erli/services/product/node_modules/undici/lib/core/util.js:894:19)
at Immediate._onImmediate (/home/konrad/erli/services/product/node_modules/undici/lib/core/util.js:863:11)
at process.processImmediate (node:internal/timers:504:21)
at process.topLevelDomainCallback (node:domain:161:15)
at process.callbackTrampoline (node:internal/async_hooks:128:24) {
code: 'UND_ERR_CONNECT_TIMEOUT'
}
> await require('undici').request('http://example.com:3113')
Uncaught AggregateError [ETIMEDOUT]:
at internalConnectMultiple (node:net:1134:18)
at afterConnectMultiple (node:net:1715:7)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
code: 'ETIMEDOUT',
[errors]: [
Error: connect ETIMEDOUT ::1:3113
at createConnectionError (node:net:1678:14)
at Timeout.internalConnectMultipleTimeout (node:net:1737:38)
at listOnTimeout (node:internal/timers:607:11)
at process.processTimers (node:internal/timers:541:7) {
errno: -110,
code: 'ETIMEDOUT',
syscall: 'connect',
address: '::1',
port: 3113
},
Error: connect ECONNREFUSED 127.0.0.1:3113
at createConnectionError (node:net:1678:14)
at afterConnectMultiple (node:net:1708:16)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 3113
}
]
}
Environment
Ubuntu 24.04, Node 24.12, undici 8.1.0 (and 7.18)
Additional context
I had retrying/error handling logic for UND_ERR_CONNECT_TIMEOUT. When ipv6 is enabled but not functional, the errors that would normally be UND_ERR_CONNECT_TIMEOUT changed into AggregateError with ETIMEDOUT and ENETUNREACH, and I didn't handle them properly (not to mention that they are harder to handle).
I see a lot of effort was put into repacking those netive errors errors from lib/core/errors.js, it would be great if one could rely on handling just those errors, to have no edge cases or have a definite guide that covers them.
Perhaps one of following proposals should be considered:
- ENETUNREACH should be suppressed when other errors exist, as ENETUNREACH is usually of lesser importance… It would protect those migrating from ipv4 to dual stack servers from unexpected error format change.
- some heuristic that packs those errors together but still exposes the details
- it should be documented that undici can reject with
AggregateError, but undici should replace ETIMEDOUT with UND_ERR_CONNECT_TIMEOUT inside AggregateError
Bug Description
When making requests that attempt connection to multiple ip addresses (like a ipv4 and a ipv6), then rejection is quite volatile:
ConnectTimeoutError, hiding reasons of previous unsuccessfull connection attemptsnode:netinsideReproducible By
setup firewall
setup address that resolves to localhost on both families: write to /etc/hosts:
run in node
Expected Behavior
I would expect both requests to reject similar payload
Logs & Screenshots
Environment
Ubuntu 24.04, Node 24.12, undici 8.1.0 (and 7.18)
Additional context
I had retrying/error handling logic for
UND_ERR_CONNECT_TIMEOUT. When ipv6 is enabled but not functional, the errors that would normally beUND_ERR_CONNECT_TIMEOUTchanged intoAggregateErrorwithETIMEDOUTandENETUNREACH, and I didn't handle them properly (not to mention that they are harder to handle).I see a lot of effort was put into repacking those netive errors errors from lib/core/errors.js, it would be great if one could rely on handling just those errors, to have no edge cases or have a definite guide that covers them.
Perhaps one of following proposals should be considered:
AggregateError, but undici should replaceETIMEDOUTwithUND_ERR_CONNECT_TIMEOUTinsideAggregateError