TimeoutError
One class for every "took too long": socket.timeout (3.10), asyncio.TimeoutError and concurrent.futures.TimeoutError (3.11) are now all aliases of it.
TimeoutError(*args)
Raised by
socket ops with settimeout(), asyncio.wait_for / asyncio.timeout, Future.result(timeout=…)
Message
'timed out' from sockets; often empty from asyncio / futures
Quick fix
retry with backoff, or raise the limit
Watch out
not a ConnectionError; subprocess.TimeoutExpired is not one either
Demo
Live evaluation
Raise it with a message. An empty message gives the bare class name — what asyncio and concurrent.futures produce.
Try:
Inputs
messagestrmessage
Code
raise TimeoutError('no reply from api.example.com after 5s')
Uncaught exception
TimeoutError: no reply from api.example.com after 5s
With an empty message the traceback line is just TimeoutError — that is what you see from asyncio.wait_for() and Future.result(timeout=…), so an empty message does not mean the error is broken. In Handle, a timeout is treated as transient: the loop retries and only reports the last error when every attempt timed out.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| *args | object | no | Usually a message ('timed out' for sockets). With (errno, strerror) it behaves like any OSError. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| args | tuple | The message, e.g. ('timed out',) for sockets; empty for asyncio/futures timeouts. |
| errno | int | None | errno.ETIMEDOUT when the OS reported the timeout (e.g. a TCP connect that never got an answer); None otherwise. |
| strerror | str | None | The OS text for ETIMEDOUT when set ('Connection timed out' on Linux). |
Common patterns
Socket timeout
settimeout() makes blocking calls raise TimeoutError instead of hanging forever.
import socket s = socket.create_connection(('example.com', 80), timeout=5) try: data = s.recv(1024) except TimeoutError: data = b''
asyncio deadline
asyncio.timeout() (3.11+) or wait_for() cancel the work and raise TimeoutError.
import asyncio async def main(): try: async with asyncio.timeout(10): await fetch_all() except TimeoutError: print('took longer than 10s')
Retry with exponential backoff
Timeouts are often transient. Retry a few times, waiting longer each time, then re-raise.
import time for attempt in range(5): try: resp = call_api() break except (TimeoutError, ConnectionError): if attempt == 4: raise time.sleep(2 ** attempt)
Examples
1. Raise with a message
raise TimeoutError('no reply after 5s')
Returns
TimeoutError: no reply after 5s2. The old names are aliases
import socket, asyncio, concurrent.futures
(socket.timeout is TimeoutError, asyncio.TimeoutError is TimeoutError, concurrent.futures.TimeoutError is TimeoutError)
Returns
(True, True, True)3. Future.result(timeout=…)
from concurrent.futures import Future
Future().result(timeout=0)
Returns
TimeoutError4. asyncio.wait_for()
import asyncio
async def main():
await asyncio.wait_for(asyncio.Event().wait(), timeout=0)
asyncio.run(main())
Returns
TimeoutError5. errno ETIMEDOUT maps to it
import errno
type(OSError(errno.ETIMEDOUT, 'Connection timed out')).__name__
Returns
'TimeoutError'6. It is an OSError, not a ConnectionError
(issubclass(TimeoutError, OSError), issubclass(TimeoutError, ConnectionError))
Returns
(True, False)7. subprocess.TimeoutExpired is separate
import subprocess
issubclass(subprocess.TimeoutExpired, TimeoutError)
Returns
FalsePitfalls
1. except ConnectionError misses timeouts
TimeoutError and ConnectionError are siblings under OSError. Network retry code usually needs both.
ConnectionError only
try: raise TimeoutError('timed out') except ConnectionError: r = 'retry' r
TimeoutError: timed out
Both classes
try: raise TimeoutError('timed out') except (ConnectionError, TimeoutError): r = 'retry' r
'retry'
2. subprocess timeouts are not TimeoutError
subprocess.run(..., timeout=…) raises subprocess.TimeoutExpired, a SubprocessError. except TimeoutError lets it through.
except TimeoutError
import subprocess try: raise subprocess.TimeoutExpired(['backup.sh'], 5) except TimeoutError: r = 'timed out' r
subprocess.TimeoutExpired: Command '['backup.sh']' timed out after 5 seconds
except TimeoutExpired
import subprocess try: raise subprocess.TimeoutExpired(['backup.sh'], 5) except subprocess.TimeoutExpired as e: r = f'{e.cmd} timed out after {e.timeout}s' r
"['backup.sh'] timed out after 5s"
3. except OSError before except TimeoutError
TimeoutError is an OSError subclass, so a preceding except OSError takes it and the timeout branch never runs.
OSError first
try: raise TimeoutError('timed out') except OSError: r = 'I/O failure' except TimeoutError: r = 'retry later' r
'I/O failure'
TimeoutError first
try: raise TimeoutError('timed out') except TimeoutError: r = 'retry later' except OSError: r = 'I/O failure' r
'retry later'
When to use
Use it
- Catching socket, asyncio and futures timeouts with one except clause
- Raising it from your own code when a deadline passes
- Retrying transient network slowness with backoff
Reach for something else
- subprocess.run(timeout=…) → catch subprocess.TimeoutExpired
- requests / httpx timeouts → their own exception classes (see the FAQ)
- queue.get(timeout=…) → raises queue.Empty, not TimeoutError
Notes
CPython impl
Objects/exceptions.c — OSError(ETIMEDOUT, …) returns TimeoutError
Aliases
socket.timeout (3.10), asyncio.TimeoutError and concurrent.futures.TimeoutError (3.11) are all TimeoutError
Catch via
except OSError — but not except ConnectionError
FAQ
Since Python 3.10 there is none: socket.timeout is a deprecated alias, socket.timeout is TimeoutError is True. Before 3.10 socket.timeout was a separate OSError subclass, so code for older versions caught socket.timeout explicitly. New code should catch TimeoutError.
History
3.3
Added, together with the other OSError subclasses (PEP 3151).
3.10
socket.timeout made an alias of TimeoutError.
3.11
asyncio.TimeoutError and concurrent.futures.TimeoutError made aliases of TimeoutError.