What Is a TimeoutError in Python?
A TimeoutError is an exception raised when an operation takes longer than a specified time limit. In Python, it is a built-in exception (inheriting from OSError) that signals a blocking I/O or network call has exceeded its allowed duration. Common scenarios include:
- HTTP requests that hang due to unresponsive servers
- Database queries that lock or run indefinitely
- Subprocesses that never complete
- Socket connections that time out during connect, send, or receive
Example of a TimeoutError when making a slow HTTP request with the requests library:
import requests
try:
# Set a 3-second timeout; if the server doesn't respond, a TimeoutError occurs
response = requests.get("https://httpbin.org/delay/10", timeout=3)
print(response.text)
except requests.exceptions.Timeout as e:
print(f"Request timed out: {e}")
Why TimeoutError Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Ignoring timeouts can lead to:
- Blocked threads – A single hanging request can freeze an entire application or web server.
- Resource exhaustion – Open connections, file handles, or database connections accumulate, eventually crashing the system.
- Poor user experience – End users face infinite loading spinners or unresponsive UIs.
- Security risks – Attackers can exploit un-timed operations to perform denial-of-service (DoS) attacks.
Proper timeout handling is essential for building robust, production-ready Python applications.
How to Fix TimeoutError: Complete Solutions
1. Network Requests (requests, urllib, aiohttp)
Using the requests library:
import requests
try:
# Explicit timeout: connect timeout + read timeout (in seconds)
response = requests.get("https://api.example.com/data", timeout=(3, 10))
response.raise_for_status()
print(response.json())
except requests.exceptions.Timeout:
print("The request timed out. Please try again later.")
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
Using urllib:
from urllib.request import urlopen
from urllib.error import URLError
import socket
try:
response = urlopen("https://httpbin.org/delay/5", timeout=2)
data = response.read()
print(data[:100])
except URLError as e:
if isinstance(e.reason, socket.timeout):
print("Connection timed out!")
else:
print(f"URL error: {e}")
Using aiohttp (asynchronous):
import asyncio
import aiohttp
async def fetch(session, url):
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as response:
return await response.text()
except asyncio.TimeoutError:
return "Request timed out"
async def main():
async with aiohttp.ClientSession() as session:
result = await fetch(session, "https://httpbin.org/delay/10")
print(result)
asyncio.run(main())
2. Database Connections (sqlite3, psycopg2, SQLAlchemy)
SQLite:
import sqlite3
import time
conn = sqlite3.connect("example.db", timeout=5) # 5-second timeout for acquiring lock
cursor = conn.cursor()
try:
# Simulate a long-running query
cursor.execute("SELECT * FROM slow_table WHERE value = 1")
rows = cursor.fetchall()
except sqlite3.OperationalError as e:
if "database is locked" in str(e):
print("Timeout: database lock could not be acquired.")
else:
print(f"Database error: {e}")
finally:
conn.close()
PostgreSQL with psycopg2:
import psycopg2
from psycopg2 import OperationalError
try:
conn = psycopg2.connect(
host="localhost",
dbname="test",
user="user",
password="pass",
connect_timeout=3 # 3-second connection timeout
)
cur = conn.cursor()
# Set statement timeout (in milliseconds)
cur.execute("SET statement_timeout = 5000") # 5 seconds
cur.execute("SELECT pg_sleep(10)") # Will raise a timeout
except OperationalError as e:
print(f"Timeout or connection error: {e}")
finally:
if conn:
conn.close()
SQLAlchemy:
from sqlalchemy import create_engine, text
engine = create_engine(
"postgresql://user:pass@localhost/test",
connect_args={"connect_timeout": 3},
pool_pre_ping=True,
pool_recycle=3600
)
with engine.connect() as conn:
try:
# Set statement timeout per session
conn.execute(text("SET statement_timeout = 4000"))
result = conn.execute(text("SELECT pg_sleep(5)"))
print(result.fetchone())
except Exception as e:
print(f"Query timed out: {e}")
3. Subprocess Calls (subprocess module)
import subprocess
import time
try:
# timeout in seconds
result = subprocess.run(
["sleep", "10"],
capture_output=True,
text=True,
timeout=5
)
print(result.stdout)
except subprocess.TimeoutExpired:
print("Subprocess timed out after 5 seconds. Killing process...")
except subprocess.CalledProcessError as e:
print(f"Subprocess failed: {e}")
4. Socket Programming
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3.0) # 3-second timeout for all socket operations
try:
sock.connect(("example.com", 80))
sock.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
data = sock.recv(4096)
print(data)
except socket.timeout:
print("Socket operation timed out.")
except socket.error as e:
print(f"Socket error: {e}")
finally:
sock.close()
Best Practices for Handling TimeoutError
- Always set explicit timeouts – Never rely on default infinite timeouts. Use sensible values based on expected operation duration (e.g., 2–10 seconds for API calls, 30–60 seconds for heavy DB queries).
- Use context managers – For resources like sockets, database connections, and HTTP sessions, always use
withstatements to ensure proper cleanup even when a timeout occurs. - Implement retry logic with exponential backoff – Transient timeouts (e.g., temporary network glitches) can be retried. Increase delay between retries to avoid overwhelming the server.
- Log timeout details – Include the operation name, timeout value, and context in logs for debugging.
- Set timeouts at multiple layers – For example, both the HTTP client and the server should enforce timeouts (e.g., using
nginxproxy_read_timeoutandgunicorn--timeout). - Test with mocks – Simulate slow or hanging operations in unit tests using
unittest.mockto verify your timeout handling works correctly. - Avoid catching bare
Exception– Catch specific exceptions likerequests.exceptions.Timeoutorsocket.timeoutto avoid hiding unrelated errors.
Example of retry logic with exponential backoff:
import time
import requests
from requests.exceptions import Timeout, ConnectionError
def fetch_with_retry(url, max_retries=3, base_delay=1):
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
except (Timeout, ConnectionError) as e:
if attempt == max_retries - 1:
raise
delay = base_delay * (2 ** attempt) # 1, 2, 4 seconds
print(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay}s...")
time.sleep(delay)
# Usage
try:
data = fetch_with_retry("https://httpbin.org/delay/10")
print(data)
except Exception as e:
print(f"All retries failed: {e}")
Conclusion
TimeoutError is a critical exception that every Python developer must handle correctly to build reliable, responsive applications. By understanding where timeouts can occur—network requests, database queries, subprocesses, and sockets—you can apply targeted solutions: set explicit timeouts, use proper exception handling, implement retries with backoff, and always clean up resources. Following the best practices outlined in this guide will help you eliminate hanging operations, prevent resource leaks, and deliver a robust user experience. Remember: a well-handled timeout is not a failure—it’s a controlled response to an unpredictable environment.