Python Central HubSign in

Python Programming / Python Networking

Networking Errors and Timeouts

Handle common networking failures - DNS issues, timeouts, connection errors, and HTTP errors.

Common networking failures

  • Timeouts: server too slow
  • Connection errors: server down, wrong port
  • DNS errors: domain name can’t be resolved
  • HTTP errors: 4xx/5xx responses

requests exceptions

Python
import requests
 
try:
    r = requests.get("https://httpbin.org/delay/10", timeout=2)
    r.raise_for_status()
    print(r.text[:100])
except requests.Timeout:
    print("Timed out")
except requests.ConnectionError:
    print("Connection error")
except requests.HTTPError as e:
    print("HTTP error:", e)
except requests.RequestException as e:
    print("Other request error:", e)

Add a timeout everywhere

  • Always set timeout=
  • Combine with retries if needed
sketch A socket has no timeout until you give it one p5.js
The default is None, which means block forever -- a request that never returns is not a hung server so much as a missing argument. Two connections measured here, one to a closed local port and one to an unreachable address: both returned at exactly the limit that was set, 2.017 seconds for a 2 second timeout and 1.008 for a 1 second one. Worth noting what did NOT happen: the closed local port did not fail fast. On this machine the firewall drops the packet rather than refusing it, so fast failure is not something to rely on.

Basic backoff idea

Python
import time
import requests
 
url = "https://httpbin.org/status/503"
 
wait = 1
for attempt in range(4):
    try:
        r = requests.get(url, timeout=5)
        r.raise_for_status()
        print("success")
        break
    except requests.RequestException:
        print("failed, sleeping", wait)
        time.sleep(wait)
        wait *= 2

Tip

For production-grade retries, use a library or configure a session with retry adapters.

Why networks fail so often

A network call depends on many things you do not control: your Wi-Fi, a DNS server, the remote machine, and every router in between. Any of them can be slow or down at any moment. Code that assumes success will eventually hang or crash, so treat failure as a normal outcome and decide in advance what your program should do.

Reading the requests example

The requests library has one base error, requests.RequestException, and more specific ones below it. The try block asks for a page that deliberately takes 10 seconds, with timeout=2:

  • requests.Timeout fires because the server did not answer in time.
  • requests.ConnectionError covers a server that is down, a wrong port, or a name that cannot be resolved (DNS).
  • raise_for_status() turns a 4xx or 5xx response into a requests.HTTPError. Without it, a 404 or 503 is a "successful" call that returns an error page, and your program would carry on with bad data.
  • The final RequestException catches everything else.

Order matters: put specific handlers first and the general one last.

Timeouts: the most important line

requests.get(url) has no timeout by default. A silent server can block your program forever. Always pass timeout=. A single number applies to connecting and to each wait for data; a tuple such as timeout=(3, 10) sets connect and read separately.

Retrying safely

The backoff example doubles its wait after each failure (1, 2, 4 seconds), so a struggling server gets breathing room. Only retry things that are safe to repeat, such as a GET. Retrying a payment POST could charge someone twice. Retry on timeouts, connection errors and 503, but not on 404 or 401, since asking again will not change the answer.

A small helper keeps this tidy:

Python
import time
import requests
 
 
def fetch(url: str, attempts: int = 3) -> str | None:
    wait = 1
    for attempt in range(1, attempts + 1):
        try:
            r = requests.get(url, timeout=5)
            r.raise_for_status()
            return r.text
        except requests.RequestException as e:
            print(f"attempt {attempt} failed: {e}")
            if attempt < attempts:
                time.sleep(wait)
                wait *= 2
    return None

It returns None after the last failure, so the caller decides what to do next.

Common mistakes

  • Calling requests.get without timeout.
  • Forgetting raise_for_status() and treating an error page as data.
  • Catching a bare except: and hiding the real problem.
  • Retrying forever with no delay, which hammers a server that is already struggling.

When to use it

Use these patterns for every call that leaves your machine. Log the failure clearly, retry a few times when it makes sense, and then give up with a helpful message.

🧪 Try It Yourself

Exercise 1 – GET Request

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 2 – POST with JSON

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 3 – Handle Timeout

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Was this page helpful?

Let us know how we did