Error: connect ETIMEDOUT is meaningfully different from ECONNREFUSED even though both are connection failures — ECONNREFUSED means something actively rejected the connection (nothing listening, or an explicit refusal), while ETIMEDOUT means the connection attempt simply never got a response at all, which points to a different category of underlying cause: network reachability, firewalls, or an overloaded/unresponsive remote server.
This error means a TCP connection attempt didn't complete within the expected time — no RST (rejection) was received, no SYN-ACK came back, nothing; the connection attempt just hung until Node's timeout expired, which usually means a network path issue (firewall silently dropping packets, incorrect routing, an unreachable host) rather than a service actively running but rejecting connections.
Why This Error Happens
TCP connection establishment requires a handshake, and if packets aren't successfully exchanged (dropped by a firewall configured to silently drop rather than actively reject, a host that's genuinely unreachable due to routing or DNS pointing at the wrong address, or a remote server too overloaded to respond to new connections in time), the connecting side eventually gives up after its configured timeout, throwing ETIMEDOUT. This is distinct from ECONNREFUSED, which requires an active response (even a negative one) from the network path.
Reproducing and Diagnosing the Error
A common cause — connecting to a host blocked by a firewall that silently drops rather than rejects:
const client = new pg.Client({ host: "db.internal.example.com", port: 5432 });
await client.connect();
// Error: connect ETIMEDOUT 10.0.1.50:5432
// (if a firewall between your app and the database silently drops the packets)
Diagnosing with a raw connectivity test, separate from your application's specific client library:
nc -zv db.internal.example.com 5432 -w 5
# Times out identically — confirms this is a network reachability issue,
# not something specific to your application's database client
telnet db.internal.example.com 5432
# Same diagnostic value — hangs rather than connecting or refusing
Core Concepts Behind This Error
A firewall configured to silently drop packets (rather than actively reject connections) is a very common cause of ETIMEDOUT specifically, as opposed to ECONNREFUSED — some security configurations deliberately drop rather than reject, since a reject response confirms to a potential attacker that something is listening, while a silent drop gives no such confirmation.
Security group or network ACL misconfiguration in cloud environments (AWS, GCP, Azure) is a common source in production, where your application's compute resource and the target service (a database, an internal API) are in different network segments and a security rule doesn't explicitly permit the needed traffic between them.
An overloaded remote server that's too busy to complete the TCP handshake in time can also produce ETIMEDOUT, distinct from the networking-layer causes above — this points toward the remote service's capacity rather than a network path issue, and needs different diagnosis (checking the remote server's load and connection handling capacity).
DNS resolving to an incorrect or stale IP address can produce ETIMEDOUT if the resolved address happens to be unreachable (rather than actively refusing, which would suggest something is at least listening at that address) — worth verifying DNS resolution independently when other causes don't explain the timeout.
Fixing "connect ETIMEDOUT"
Fix 1: Verify network path connectivity independent of your application, using nc or telnet to isolate whether the issue is network-level or application-specific:
nc -zv target-host 5432 -w 5
Fix 2: Check and correct firewall/security group rules to explicitly permit traffic between your application and the target service, particularly in cloud environments where network segmentation is common:
# AWS example: verify a security group rule permits inbound traffic
# from your application's security group on the target port
aws ec2 describe-security-groups --group-ids sg-xxxxxxx
Fix 3: Verify DNS resolves to the expected, correct address, ruling out a stale or misconfigured DNS entry as the actual cause:
dig target-host
# or: nslookup target-host
Fix 4: Implement connection retry with backoff for cases where the cause is a transient remote server overload, distinct from a persistent network path issue that retries won't resolve:
async function connectWithRetry(config: ClientConfig, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
const client = new pg.Client(config);
await client.connect();
return client;
} catch (err: any) {
if (err.code !== "ETIMEDOUT" || i === attempts - 1) throw err;
await new Promise((r) => setTimeout(r, 2000 * (i + 1)));
}
}
throw new Error("unreachable");
}
Is ETIMEDOUT Something a Retry Can Fix, or Always a Network Configuration Issue?
Retry meaningfully helps only for the transient remote-overload case, where the target service is genuinely reachable but momentarily too busy to respond — retrying against a genuinely blocked network path (firewall silently dropping, incorrect security group rules) will fail identically every time, since retries don't address a structural network reachability problem. Diagnose with a raw connectivity test first to determine which category you're actually facing before deciding whether retry logic is the right response.
Preventing ETIMEDOUT Errors in Production
Verify network path connectivity (firewall rules, security groups, DNS resolution) explicitly whenever deploying a new service dependency or changing network topology, rather than discovering misconfiguration through production timeout errors. Implement retry logic with backoff specifically for cases where transient remote overload is a plausible cause, but treat a persistent, consistent ETIMEDOUT as a signal to investigate network configuration directly rather than adding more retries.
If you hit this error, run a raw connectivity test (nc or telnet) outside your application first — it immediately tells you whether you're facing a network reachability issue or something specific to your application's connection handling, which determines the entire direction of your fix.