ConnectionResetError: [Errno 104] Connection reset by peer

Python · updated 2026-11-04

Raised when the remote end resets the connection while your code reads or writes. With requests and urllib3 it arrives wrapped as ConnectionError: ('Connection aborted.', ConnectionResetError(104, ...)). It is usually intermittent: most calls work, and the failures cluster after idle periods or under load.

Traceback (most recent call last):
  File "/app/sync/vendor.py", line 58, in fetch_orders
    resp = session.get(url, timeout=10)
  ...
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))

Quick fix

Retry idempotent requests (GET, PUT, DELETE) a few times with backoff by mounting an HTTPAdapter with urllib3 Retry on your session. If it happens after idle periods, the server or load balancer closed a keep-alive connection your pool reused; retries cover that. If every call fails, check the URL scheme and port, TLS, and whether the remote is rate limiting you.

  1. Note whether it is intermittent (reused idle connections, load) or every call (wrong port, TLS, firewall, blocked IP).
  2. Mount an HTTPAdapter with Retry(total=3, backoff_factor=0.5, allowed_methods=...) on the session.
  3. Only retry POSTs that are safe to repeat, or that carry an idempotency key.
  4. Set a timeout on every call so a dead connection fails fast instead of hanging.
  5. If it started with a deploy, compare TLS versions, proxies and the target host between environments.
python
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(
    total=3,
    backoff_factor=0.5,                       # 0.5s, 1s, 2s
    status_forcelist=(502, 503, 504),
    allowed_methods=("GET", "HEAD", "PUT", "DELETE", "OPTIONS"),
)
session = requests.Session()
session.mount("https://", HTTPAdapter(max_retries=retry))

resp = session.get("https://api.example.com/orders", timeout=10)

Why it happens

Stale keep-alive connection

The pool reuses a connection the server or a load balancer closed after its idle timeout; the first request on it gets a reset.

The server or proxy dropped the request

A crash, a restart during a deploy, a request body over a size limit, or a proxy timeout resets the connection instead of answering.

Rate limiting or blocking

Some APIs and firewalls reset connections instead of returning 429 when a client sends too much or comes from a blocked range.

Protocol mismatch

Plain HTTP to a TLS port (or the reverse), or an old TLS version the server refuses, can show up as a reset on the first request.

Other fixes

  • For httpx, pass transport=httpx.HTTPTransport(retries=3), which retries connection failures.
  • On Windows the same thing reads ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host.

Same error, other wording

  • requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))
  • urllib3.exceptions.ProtocolError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))
  • ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host

Let your coding agent catch the next one

Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:

> Find the "Connection reset by peer" errors in production, tell me which host they come from and whether they are intermittent, and add retries with backoff for the idempotent calls to that host.

Plans from $29/moSee what your agent readsSee the setupCancel any time from the billing portal

Related errors

More in Network and HTTP

References: Python docs: built-in exceptions (ConnectionResetError), urllib3 docs: Retry, requests docs: transport adapters