ERR_CONNECTION_TIMED_OUT / ETIMEDOUT (tested)

ERR_CONNECTION_TIMED_OUT (Chrome) and ETIMEDOUT (Node, Linux) mean the TCP connection was never set up. Packets went out and nothing came back. We pointed four clients at an address that drops every packet and timed exactly how long each waited and what it said.

1. What the error means

To open a connection, the client sends a SYN packet and waits for a SYN-ACK. If nothing at all comes back, the client resends the SYN a few times and then gives up. That is a connection timeout.

It is different from a refused connection (ECONNREFUSED; see ERR_CONNECTION_REFUSED / ECONNREFUSED). Refused means the host answered with a reset because nothing listens on the port. That fails at once. A timeout means silence: the packets are dropped by a firewall, a wrong route, a down host or a wrong IP.

It is also different from a server that accepts the connection and then never answers. That gives other errors, or none at all (section 4).

2. Our test setup

We connected to http://10.255.255.1/. It is a private address that nothing on our network answers, so every SYN is silently dropped. 192.0.2.1 (TEST-NET-1, reserved for documentation) did the same. No firewall rules were changed. Each client ran with its defaults, then with an explicit timeout.

3. Results: blackholed address

ClientErrorTime to error
Chromium 153, page navigationnet::ERR_CONNECTION_TIMED_OUT134.7 s
Chromium 153, fetch() in a pageTypeError: Failed to fetch135.1 s
curl, no optionsexit 28, "Failed to connect to 10.255.255.1 port 80 after 134745 ms: Could not connect to server"134.7 s
curl --connect-timeout 5exit 28, "Connection timed out after 5002 milliseconds"5.0 s
Node 24 net.connect()connect ETIMEDOUT 10.255.255.1:80, code ETIMEDOUT, errno -110134.7 s
Node 24 fetch()TypeError: fetch failed, cause ConnectTimeoutError, code UND_ERR_CONNECT_TIMEOUT, "Connect Timeout Error (attempted address: 10.255.255.1:80, timeout: 10000ms)"10.5 s
Python urllib, no timeoutURLError(TimeoutError(110, 'Connection timed out'))134.7 s
Python urllib timeout=5URLError(TimeoutError('timed out'))5.0 s

The 134.7 s is not a browser or library setting. It is the Linux kernel giving up after its default SYN retries. Chromium, curl, Node net and Python all hit the same wall within a few milliseconds of each other. curl's own default connect timeout is 300 s (per the curl docs, not measured), so on Linux the kernel always wins.

Node's built-in fetch is the exception. Its undici connect timeout of 10 s fired first. Catch it like this:

try {
  await fetch(url);
} catch (e) {
  if (e.cause?.code === 'UND_ERR_CONNECT_TIMEOUT') { /* host unreachable */ }
  if (e.cause?.code === 'ETIMEDOUT') { /* kernel timeout */ }
}

Note that Chromium's fetch() only says "Failed to fetch". Page code cannot see the reason. The DevTools console and network panel show the net:: code.

4. Contrast: the server accepts but never answers

Next we ran a Node server on 127.0.0.1 that accepts connections and never sends a byte. One version was a raw TCP server, one an HTTP server whose handler never calls res.end(). Both gave the same client results.

ClientResultTime
curl -m 3exit 28, "Operation timed out after 3003 milliseconds with 0 bytes received", status 0003.0 s
curl --connect-timeout 2 -m 5exit 28, "Operation timed out after 5002 milliseconds with 0 bytes received"5.0 s (connect timeout never fired)
Python urllib timeout=3TimeoutError('timed out') (not wrapped in URLError)3.0 s
Node 24 fetch(), defaultsfetch failed, cause HeadersTimeoutError, code UND_ERR_HEADERS_TIMEOUT300.8 s
Chromium 153 navigationNo error from the browser. Playwright's own 420 s limit fired first.> 420 s

So ERR_CONNECTION_TIMED_OUT and ETIMEDOUT never appear here. The connection worked. curl's --connect-timeout does not help either; only -m/--max-time caps the whole request. We did not see Chromium's net::ERR_TIMED_OUT in this test. Chromium simply kept waiting for at least 7 minutes.

On the server side, Node's headersTimeout (60 s) and requestTimeout (300 s) only cover receiving the request. They did nothing to stop our handler hanging. When a client connected and sent nothing, or only half the headers, Node replied HTTP/1.1 408 Request Timeout and closed after 77 s (60 s plus the 30 s check interval). See 408 Request Timeout.

Behind a proxy, a hanging upstream turns into 504 Gateway Timeout. If the client gives up first, nginx logs 499.

5. Common causes

6. How to fix it

If connections open and then drop, read ERR_CONNECTION_RESET / ECONNRESET.

Frequently asked questions

How long does Chrome wait before ERR_CONNECTION_TIMED_OUT?

On our Linux box, 134.7 s. That is the kernel's default SYN retry limit, not a Chrome setting. curl, Node net.connect and Python with no timeout failed at the same moment.

Why does Node fetch fail after 10 seconds?

Node's built-in fetch (undici) has its own 10 s connect timeout. We measured 10.5 s and the cause code UND_ERR_CONNECT_TIMEOUT. Plain net.connect has no such limit and waited 134.7 s for the kernel's ETIMEDOUT.

What is the difference between ETIMEDOUT and ECONNREFUSED?

ETIMEDOUT: no reply at all, usually a firewall drop, wrong IP or down host. ECONNREFUSED: the host replied that nothing listens on the port. Refused fails at once; timed out takes seconds to minutes.

Does curl --connect-timeout stop a server that never answers?

No. It only covers setting up the connection. Against our server that accepted but never replied, curl --connect-timeout 2 -m 5 ran the full 5 s and failed with Operation timed out, 0 bytes received. Use -m (--max-time).

Does ERR_TIMED_OUT appear when the server hangs?

Not in our test. Chromium 153 waited over 7 minutes on a server that accepted but never replied, without any error. The connection succeeded, so ERR_CONNECTION_TIMED_OUT does not apply there either.

Written with AI assistance. Measured on 2026-10-02 on Linux 6.12 with headless Chromium 153.0.8010.12 (Playwright), Node.js 24.21.0, curl 8.14.1 and Python 3.13.5 urllib. Blackholed target: 10.255.255.1:80 (no reply to SYN; 192.0.2.1 behaved the same). Hanging-server tests used a local Node server that accepts but never answers. Kernel and firewall settings were left at their defaults. PlainHeaders is our extension; curl, Python, Node.js and Chromium are free.