The Fix
pip install urllib3==1.25.9
Based on closed urllib3/urllib3 issue #1808 · PR/commit linked
Production note: This tends to surface only under concurrency. Reproduce with load tests and watch for lock contention/cancellation paths.
@@ -297,5 +297,8 @@ In chronological order:
* Test improvements
+* Chris Olufson <[email protected]>
+ * Fix for connection not being released on HTTP redirect and response not preloaded
+
import logging
from pathlib import Path
import shutil
import urllib3
_logger = logging.getLogger(__name__)
_BUFFER_SIZE = 1024 * 1024 # buffer for downloading remote resource
url_headers = urllib3.make_headers(keep_alive=True, accept_encoding=True)
url_retries = urllib3.Retry(total=2, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
url_client = urllib3.PoolManager(timeout=urllib3.Timeout(total=15.0), retries=url_retries, block=True,
headers=url_headers)
def stream_response(url: str, filepath: Path, id: int):
# Must call release_conn() after file copied but opening/writing exception is possible
rsp = None
try:
_logger.debug(f'> {id:4d} GET: {url}')
rsp = url_client.request('GET', url, preload_content=False)
_logger.debug(f'> {id:4d} resp code: {rsp.status}')
if rsp.status == 200:
_logger.debug(f'> {id:4d} writing: "{filepath.name}"')
with filepath.open('wb', buffering=_BUFFER_SIZE) as rfp:
shutil.copyfileobj(rsp, rfp, length=_BUFFER_SIZE)
except Exception as ex:
_logger.exception(f'> {id:4d} Error')
finally:
if rsp:
rsp.release_conn()
return rsp.status
logging.basicConfig(level=logging.DEBUG,
handlers=[logging.FileHandler('output.log', mode='w'), logging.StreamHandler()])
logging.captureWarnings(True)
_logger.info('Started')
items = [
['https://fwd.aws/v3mz8', Path('d1.pdf')],
['https://fwd.aws/88JKk', Path('d2.pdf')],
['https://fwd.aws/D9yDG', Path('d3.pdf')],
['https://fwd.aws/NmXza', Path('d4.pdf')],
['https://fwd.aws/NmXza', Path('d5.pdf')]
]
for id, it in enumerate(items):
stream_response(it[0], it[1], id)
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Upgrade to fixed release\npip install urllib3==1.25.9\nWhen NOT to use: Do not use this fix if your application relies on blocking behavior for connection management.\n\nOption C — Workaround\ndoes not help for you. Are you sure you're using the exact example in your first post?\nWhen NOT to use: Do not use this fix if your application relies on blocking behavior for connection management.\n\n
Why This Fix Works in Production
- Trigger: urllib3 hanging inside call to PoolManager request
- Mechanism: The connection was not properly released after HTTP redirects, causing hangs
- Why the fix works: Addresses the issue of urllib3 hanging inside PoolManager requests by ensuring that connections are properly drained and released after redirects. (first fixed release: 1.25.9).
- If left unfixed, failures can be intermittent under concurrency (hard to reproduce; shows up as sporadic 5xx/timeouts).
Why This Breaks in Prod
- Shows up under Python 3.8.2 in real deployments (not just unit tests).
- The connection was not properly released after HTTP redirects, causing hangs
- Production symptom (often without a traceback): urllib3 hanging inside call to PoolManager request
Proof / Evidence
- GitHub issue: #1808
- Fix PR: https://github.com/urllib3/urllib3/pull/1817
- First fixed release: 1.25.9
- Reproduced locally: No (not executed)
- Last verified: 2026-02-09
- Confidence: 0.75
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.45
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“Thanks for the full example! Can you try setting block=False when instantiating urllib3.PoolManager to see if it helps?”
“Hi @pquentin I tried setting block=False as you suggested, and it made no difference. The call to urllib3.PoolManager request still hangs and never returns”
“@tycarac That's great to hear! I'd be happy to assist if you have any difficulties”
“Lots of firsts indeed! Congratulations on the progress! For your next pull request you can try following https://guides.github.com/activities/hello-world/ but for this one can you please…”
Failure Signature (Search String)
- urllib3 hanging inside call to PoolManager request
- I have a repeatable issue with urllib3 hanging inside PoolManager request for an unusual edge case. I have repeated this issue on two separate machines.
Copy-friendly signature
Failure Signature
-----------------
urllib3 hanging inside call to PoolManager request
I have a repeatable issue with urllib3 hanging inside PoolManager request for an unusual edge case. I have repeated this issue on two separate machines.
Error Message
Signature-only (no traceback captured)
Error Message
-------------
urllib3 hanging inside call to PoolManager request
I have a repeatable issue with urllib3 hanging inside PoolManager request for an unusual edge case. I have repeated this issue on two separate machines.
Minimal Reproduction
import logging
from pathlib import Path
import shutil
import urllib3
_logger = logging.getLogger(__name__)
_BUFFER_SIZE = 1024 * 1024 # buffer for downloading remote resource
url_headers = urllib3.make_headers(keep_alive=True, accept_encoding=True)
url_retries = urllib3.Retry(total=2, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
url_client = urllib3.PoolManager(timeout=urllib3.Timeout(total=15.0), retries=url_retries, block=True,
headers=url_headers)
def stream_response(url: str, filepath: Path, id: int):
# Must call release_conn() after file copied but opening/writing exception is possible
rsp = None
try:
_logger.debug(f'> {id:4d} GET: {url}')
rsp = url_client.request('GET', url, preload_content=False)
_logger.debug(f'> {id:4d} resp code: {rsp.status}')
if rsp.status == 200:
_logger.debug(f'> {id:4d} writing: "{filepath.name}"')
with filepath.open('wb', buffering=_BUFFER_SIZE) as rfp:
shutil.copyfileobj(rsp, rfp, length=_BUFFER_SIZE)
except Exception as ex:
_logger.exception(f'> {id:4d} Error')
finally:
if rsp:
rsp.release_conn()
return rsp.status
logging.basicConfig(level=logging.DEBUG,
handlers=[logging.FileHandler('output.log', mode='w'), logging.StreamHandler()])
logging.captureWarnings(True)
_logger.info('Started')
items = [
['https://fwd.aws/v3mz8', Path('d1.pdf')],
['https://fwd.aws/88JKk', Path('d2.pdf')],
['https://fwd.aws/D9yDG', Path('d3.pdf')],
['https://fwd.aws/NmXza', Path('d4.pdf')],
['https://fwd.aws/NmXza', Path('d5.pdf')]
]
for id, it in enumerate(items):
stream_response(it[0], it[1], id)
Environment
- Python: 3.8.2
What Broke
Requests hang indefinitely during PoolManager calls, leading to application stalls.
Why It Broke
The connection was not properly released after HTTP redirects, causing hangs
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
pip install urllib3==1.25.9
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Option C — Workaround Temporary workaround
does not help for you. Are you sure you're using the exact example in your first post?
Use only if you cannot change versions today. Treat this as a stopgap and remove once upgraded.
Fix reference: https://github.com/urllib3/urllib3/pull/1817
First fixed release: 1.25.9
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- Do not use this fix if your application relies on blocking behavior for connection management.
Verify Fix
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 stress test that runs high-concurrency workloads and fails on thread dumps / blocked locks.
- Enable watchdog dumps in prod (faulthandler, thread dump endpoint) to capture deadlocks quickly.
Version Compatibility Table
| Version | Status |
|---|---|
| 1.25.9 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.