ConnectionError, what it means and how to fix it

ConnectionError is a family name. Python's built-in ConnectionError groups the socket-level failures (refused, reset, aborted, broken pipe), and the requests library raises its own ConnectionError wrapping everything that went wrong before an HTTP response existed, including DNS failures and refused connections. Either way, the top line is a category; the nested cause inside the message (the urllib3 or OSError text after the colon) is the specific failure to fix.

What it looks like

import requests
requests.get("http://localhost:8000/api")   # nothing is
                                            # listening there

Traceback (most recent call last):
  File "main.py", line 2, in <module>
requests.exceptions.ConnectionError:
HTTPConnectionPool(host='localhost', port=8000):
Max retries exceeded ... [Errno 111] Connection refused

# the tail is the diagnosis: refused = host reachable,
# port closed; start the server or fix the port

The fix, in order

  1. Read to the end of the message. The wrapper names the pool and URL; the cause at the tail (NameResolutionError, Errno 111 refused, Errno 104 reset, an SSL error) is the actual error. Fix that, not the wrapper.
  2. Connection refused means the host answered and the port is closed: the server is not running, is listening on another port, or binds only a different interface. Start it, match the port, and check with a browser or curl against the same address.
  3. Name resolution failures mean DNS never produced an address: a typo'd host, a private name used off its network, or broken local DNS. Test the name with nslookup; the code cannot fix what the resolver cannot find.
  4. Resets and aborts mid-request point at the path: proxies, VPNs, and firewalls killing connections, or a server dying under the request. Compare from another network to locate which side of the path owns it.
  5. Check proxy environment variables on managed machines. requests honors HTTP_PROXY/HTTPS_PROXY; a stale corporate proxy makes every call fail identically. echo $HTTPS_PROXY (and the NO_PROXY list) explains failures that skip some hosts and hit others.
  6. For code that must survive flaky servers, retry with backoff and a timeout: a ConnectionError on a service that is restarting is a timing fact, not a verdict. Bound the retries and log the inner cause each time.

If it still happens

If connections fail only under load (parallel calls, test suites), the bottleneck is usually connection limits: the server's accept queue, ephemeral ports on your machine, or a pool sized for serial use. The error is the same; the remedy is pooling discipline and limits, not the single-request checks above.

Common questions

Why does requests raise ConnectionError for a DNS problem? Because from the caller's perspective the outcomes are identical: no connection, no response. requests groups every pre-response failure into ConnectionError and chains the precise cause inside it, which is why reading the full chained message is step one.

Is ConnectionError the same as the built-in ConnectionError? Same name, different classes. The built-in (a subclass of OSError) comes from socket code directly; requests.exceptions.ConnectionError is that library's own class. Catch the one your code actually raises; importing the wrong one catches nothing.

Related errors

TimeoutError · BrokenPipeError · OSError