ConnectionResetError: [Errno 104] Connection reset by peer (tested)
ConnectionResetError: [Errno 104] Connection reset by peer means the other side aborted the TCP connection (sent RST) while you were reading or writing. We reproduced it with a local server, showed the requests version (Connection aborted.), and tested a retry with backoff.
1. Reproduce it: the other side aborts the connection
import socket, struct, threading
srv = socket.create_server(("127.0.0.1", 8965))
def handler():
conn, _ = srv.accept()
conn.recv(100)
# close with SO_LINGER 0: the kernel sends RST instead of a normal FIN
conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0))
conn.close()
threading.Thread(target=handler).start()
c = socket.create_connection(("127.0.0.1", 8965))
c.sendall(b"GET / HTTP/1.0\r\n\r\n")
c.recv(100)
Output:
Traceback (most recent call last):
File "/tmp/cr370/b1.py", line 14, in <module>
c.recv(100)
~~~~~~^^^^^
ConnectionResetError: [Errno 104] Connection reset by peer
A reset means the peer sent a TCP RST instead of closing normally. Here the server forces one with SO_LINGER 0. In real life the usual causes are a server process that crashed or was restarted mid-request, a proxy, load balancer or firewall dropping an idle connection, the server rejecting the request (too large, bad TLS/protocol, rate limit) by cutting it off, or reusing a keep-alive connection the server already timed out.
2. It is ConnectionResetError, a ConnectionError
import errno, socket, struct, threading
srv = socket.create_server(("127.0.0.1", 8966))
def handler():
conn, _ = srv.accept(); conn.recv(100)
conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0)); conn.close()
threading.Thread(target=handler).start()
c = socket.create_connection(("127.0.0.1", 8966))
c.sendall(b"hello")
try:
c.recv(100)
except ConnectionResetError as e:
print(type(e).__name__, e.errno, errno.errorcode[e.errno])
print("ConnectionError?", isinstance(e, ConnectionError), "| OSError?", isinstance(e, OSError))
Output:
ConnectionResetError 104 ECONNRESET
ConnectionError? True | OSError? True
Errno 104 (ECONNRESET) maps to ConnectionResetError, so except ConnectionResetError or except ConnectionError both catch it.
3. Through requests: Connection aborted
import socket, struct, threading, http.server, requests
class H(http.server.BaseHTTPRequestHandler):
def log_message(self, *a): pass
def do_GET(self):
self.connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0))
self.connection.close() # drop the client without a response
srv = http.server.HTTPServer(("127.0.0.1", 8967), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
try:
requests.get("http://127.0.0.1:8967/", timeout=5)
except requests.exceptions.ConnectionError as e:
print(type(e).__name__ + ":", e)
Output:
ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))
requests reports it as requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, ...)). The real cause is the server side: check its logs for a crash, a timeout or a rejected request at that moment.
4. Retry with backoff (for transient resets only)
import socket, struct, threading, time
srv = socket.create_server(("127.0.0.1", 8964))
def handler():
while True:
conn, _ = srv.accept(); conn.recv(100)
conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0)); conn.close()
threading.Thread(target=handler, daemon=True).start()
def fetch(retries=3):
for attempt in range(1, retries + 1):
try:
c = socket.create_connection(("127.0.0.1", 8964), timeout=5)
c.sendall(b"ping"); return c.recv(100)
except ConnectionResetError as e:
print(f"attempt {attempt}: {e}")
time.sleep(0.1 * 2 ** attempt) # back off before retrying
return None
print("result:", fetch())
Output:
attempt 1: [Errno 104] Connection reset by peer
attempt 2: [Errno 104] Connection reset by peer
attempt 3: [Errno 104] Connection reset by peer
result: None
Retrying helps when resets are transient (an idle connection dropped by a proxy, a server restart). Against a server that resets every request, as here, retries just fail three times; that points to a request the server refuses, so fix the request instead of retrying harder. With requests, a Session plus urllib3.util.Retry on the HTTPAdapter does the same.
Related: ECONNRESET (Node/browser) · [Errno 32] Broken pipe · requests exceptions list · socket errors and errno codes.
Frequently asked questions
What does Connection reset by peer mean in Python?
The remote side aborted the TCP connection with an RST: it crashed, restarted, rejected the request, or a proxy/firewall dropped the connection.
How do I catch Errno 104?
except ConnectionResetError (a subclass of ConnectionError and OSError). With requests, catch requests.exceptions.ConnectionError.
Should I retry on Connection reset by peer?
Only for transient resets. If every request is reset, the server is refusing the request; check its logs instead.
Written with AI assistance. Measured on 2026-10-23 with Python 3.13.5 and requests 2.32.3 on Linux, against local servers. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python is free.