Guides

Timeouts: ETIMEDOUT, deadlines, and hung requests

"Timed out" never says what timed out. Every request crosses several separately timed stages — DNS, TCP connect, TLS, sending, waiting for first byte, reading the body — and the fix depends entirely on which timer fired.

Which timer fired?

Connect timeout (ETIMEDOUT during dial)SYN packets went unanswered. Unlike ECONNREFUSED (fast, active rejection), this is silence — usually a firewall dropping packets, a wrong IP, or an unreachable network. More time will not help.
Read / response timeoutConnected fine, then the server took too long to answer: slow query, cold start, overload, or a genuinely long operation with too small a budget.
Idle/socket timeoutData stopped flowing mid-body. Long streams and large downloads through proxies hit this — every hop (client, proxy, load balancer, server) has its own idle limit and the smallest one wins.
Deadline exceeded (gRPC, context)A whole-operation budget expired, possibly propagated from a caller. The timeout you see may have been set several services upstream.

Diagnose with stage timings

curl -w 'dns %{time_namelookup}  connect %{time_connect}  tls %{time_appconnect}  ttfb %{time_starttransfer}  total %{time_total}\n' \
  -o /dev/null -s https://api.example.com/slow

One line tells you where the time went. High connect: network/firewall. High ttfb: the server is slow — profile it, don't tune the client. Total fine but your app times out anyway: your client's budget is smaller than reality, or an intermediate proxy (common default: 30–60s) is cutting in before your own timer.

Choosing budgets

No timeout is a defect — a hung dependency then hangs you, and thread or connection pools drain until everything is "down" from one slow backend. Set explicit budgets on every external call: tight for connect (1–3s — dialing is fast when it works), generous only for the specific slow operations that earn it. Retry only on failures that happened before the server could act (connect timeouts), or on idempotent requests — a response timeout does not mean the work didn't happen, and blind retries double-charge and pile load onto an already-slow server. Under sustained latency, prefer failing fast (circuit breaking) over queueing more waiting requests.

Documented occurrences

145 analyzed errors across 30 libraries match this failure class. Each links to the thrown message, its source line, and documented fixes.

apache/kafka

mongodb/node-mongodb-native

pika/pika

redis/redis-py

urllib3/urllib3

celery/celery

docker/cli

go-redis/redis

gohugoio/hugo

aio-libs/aiohttp

…and 20 more libraries — search for your exact message.

Other failure classes