Jump to solution
Verify

The Fix

pip install urllib3==2.5.0

Based on closed urllib3/urllib3 issue #3581 · PR/commit linked

Jump to Verify Open PR/Commit
@@ -0,0 +1 @@ @@ -0,0 +1 @@ +Raise exception for HTTPResponse.shutdown on a connection already released to the pool. \ No newline at end of file diff --git a/src/urllib3/response.py b/src/urllib3/response.py
repro.py
import urllib3 with urllib3.HTTPSConnectionPool("urllib3.readthedocs.io") as pool: resp1 = pool.urlopen("GET", "https://urllib3.readthedocs.io/", preload_content=False) resp1.drain_conn() resp1.release_conn() resp2 = pool.urlopen("GET", "https://urllib3.readthedocs.io/", preload_content=False) resp1.shutdown() resp2.read() resp2.close()
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
Option A — Upgrade to fixed release\npip install urllib3==2.5.0\nWhen NOT to use: Do not use this fix if the connection is still in use by another response.\n\n

Why This Fix Works in Production

  • Trigger: chunk_left = self._read_next_chunk_size()
  • Mechanism: Calling shutdown on a response terminates its connection even if already returned to the pool
  • Why the fix works: Raises an exception when attempting to shutdown a connection that has already been released to the pool. (first fixed release: 2.5.0).
Production impact:
  • If left unfixed, this can cause silent data inconsistencies that propagate (bad cache entries, incorrect downstream decisions).

Why This Breaks in Prod

  • Shows up under Python 3.12.3 in real deployments (not just unit tests).
  • Calling shutdown on a response terminates its connection even if already returned to the pool
  • Surfaces as: Traceback (most recent call last):

Proof / Evidence

  • GitHub issue: #3581
  • Fix PR: https://github.com/urllib3/urllib3/pull/3601
  • First fixed release: 2.5.0
  • Reproduced locally: No (not executed)
  • Last verified: 2026-02-09
  • Confidence: 0.85
  • Did this fix it?: Yes (upstream fix exists)
  • Own content ratio: 0.40

Discussion

High-signal excerpts from the issue thread (symptoms, repros, edge-cases).

“### Subject It appears that calling shutdown on a response will terminate that response's connection, even if that connection has already been returned to the pool and some other response is using it. ### Environment ### Steps to Reproduce”
Issue thread · issue description · source

Failure Signature (Search String)

  • chunk_left = self._read_next_chunk_size()

Error Message

Stack trace
error.txt
Error Message ------------- Traceback (most recent call last): File "/usr/lib/python3.12/http/client.py", line 579, in _get_chunk_left chunk_left = self._read_next_chunk_size() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/http/client.py", line 546, in _read_next_chunk_size return int(line, 16) ^^^^^^^^^^^^^ ValueError: invalid literal for int() with base 16: b'\x17\x03\x03\x05j$)wm\xfa\x02\x87\x0b1C\x85\x89\x18S\x85S\xb3\x88\xb0\xd2c"\xc9\xbc\xc8\xd4\x9d\x85\xe8\xc4\xe6\xb7\xad7\xb0\x03\xb4k\xe4\x01\xd4\xe8~\xc9\xe5\xef\x81\x1b\xffs$\xfb\xa9\x19S \xd0{\xc During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3.12/http/client.py", line 595, in _read_chunked while (chunk_left := self._get_chunk_left()) is not None: ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/http/client.py", line 581, in _get_chunk_left raise IncompleteRead(b'') http.client.IncompleteRead: IncompleteRead(0 bytes read) The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/tmp/venv/lib/python3.12/site-packages/urllib3/response.py", line 754, in _error_catcher yield File "/tmp/venv/lib/python3.12/site-packages/urllib3/response.py", line 879, in _raw_read data = self._fp_read(amt, read1=read1) if not fp_closed els ... (truncated) ...

Minimal Reproduction

repro.py
import urllib3 with urllib3.HTTPSConnectionPool("urllib3.readthedocs.io") as pool: resp1 = pool.urlopen("GET", "https://urllib3.readthedocs.io/", preload_content=False) resp1.drain_conn() resp1.release_conn() resp2 = pool.urlopen("GET", "https://urllib3.readthedocs.io/", preload_content=False) resp1.shutdown() resp2.read() resp2.close()

Environment

  • Python: 3.12.3
  • urllib3: 2.3.0

What Broke

Attempting to read from a response raises an IncompleteRead exception due to connection shutdown.

Why It Broke

Calling shutdown on a response terminates its connection even if already returned to the pool

Fix Options (Details)

Option A — Upgrade to fixed release Safe default (recommended)

pip install urllib3==2.5.0

When NOT to use: Do not use this fix if the connection is still in use by another response.

Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.

Fix reference: https://github.com/urllib3/urllib3/pull/3601

First fixed release: 2.5.0

Last verified: 2026-02-09. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • Do not use this fix if the connection is still in use by another response.

Verify Fix

verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.

Did This Fix Work in Your Case?

Quick signal helps us prioritize which fixes to verify and improve.

Prevention

  • Add a CI check that diffs key outputs after upgrades (OpenAPI schema snapshots, JSON payload shapes, CLI output).
  • Upgrade behind a canary and run integration tests against the canary before 100% rollout.
  • Add a TLS smoke test that performs a real handshake in CI (include CA bundle validation and hostname checks).
  • Alert on handshake failures by error string and endpoint to catch cert/CA changes quickly.

Version Compatibility Table

VersionStatus
2.5.0 Fixed

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.