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 timeout | Connected 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 timeout | Data 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
- Timeout needs to be greater than 0
- The specified value of default.api.timeout.ms must be no smaller than the value of request.timeout.ms.
- The timeout cannot be negative.
- Timeout of {}ms expired before the position for partition {} could be determined
- Timeout of {}ms expired before the last committed offset for partitions {} could be determined. Try tuning default.api.timeout.ms larger to relax the threshold.
- +18 more in kafka
mongodb/node-mongodb-native
- KMS request timed out
- OIDC callback timed out after ${AUTOMATED_TIMEOUT_MS}ms.
- OIDC callback timed out after ${HUMAN_TIMEOUT_MS}ms.
- Server reported a timeout error
- Timed out during connection checkout
- +15 more in node-mongodb-native
pika/pika
- Timeout closed before call
- work_queue_put_timeout must be a number of seconds, not None; an infinite timeout lets a full work queue block the IOLoop thread forever
- work_queue_put_timeout must be a positive number of seconds, got {put_timeout!r}
- work queue full for {self._put_timeout} seconds (maxsize={self._queue.maxsize})
- {method_name} timed out after {timeout} seconds
- +12 more in pika
redis/redis-py
- Timeout reading from socket
- Timeout reading from socket
- Timed out closing connection after {self.socket_connect_timeout}
- Timeout writing to socket
- Timeout reading from {host_error}
- +8 more in redis-py
urllib3/urllib3
- Connection to {self.host} timed out. (connect timeout={self.timeout})
- Read timed out. (read timeout={timeout_value})
- Read timed out. (read timeout={read_timeout})
- The read operation timed out
- select timed out
- +6 more in urllib3
celery/celery
- Timed out waiting for a broker connection after {timeout}s. All {pool_limit} connections are in use. Consider increasing broker_pool_limit (currently {pool_limit}) or broker_pool_acquire_timeout (currently {timeout}s).
- Timed out waiting for a broker producer after {timeout}s. All {pool_limit} producer slots are in use. Consider increasing broker_pool_limit (currently {pool_limit}) or broker_pool_acquire_timeout (currently {timeout}s).
- The operation timed out.
- The operation timed out.
- Operation timed out ({timeout})
- +2 more in celery
docker/cli
- --health-timeout cannot be negative
- conflicting options: cannot specify both --timeout and --time
- timeout waiting for stats
- conflicting options: cannot specify both --timeout and --time
- timed out waiting for device token
- +1 more in cli
go-redis/redis
- redis: connection pool timeout
- redis: timed out trying to mark connection as unusable
- relaxed timeout must be greater than 0
- circuit breaker reset timeout must be >= 0
- redis: autopipeline: Close timed out after %s with %s still in flight; they hold pooled connections until the server or the OS ends them (most often a blocking command with no timeout, or ReadTimeout disabled)
- +1 more in redis
gohugoio/hugo
- failed to parse timeout: %s
- invalid timeout for resource %s: %w
- retry timeout (configured to %s) fetching remote resource: %s
- timed out rendering the page content. Extend the `timeout` limit in your Hugo config file: %w
- timeout
aio-libs/aiohttp
- Connection timeout to host {req.url}
- timeout parameter cannot be of {type(timeout)} type, please use 'timeout=ClientTimeout(...)'
- total timeout must be a positive number or None to disable, got 0. Using 0 to disable timeouts is no longer supported, use None instead.
- keepalive_timeout cannot be set if force_close is True
- Timeout context manager should be used inside a task
…and 20 more libraries — search for your exact message.