The Fix
pip install urllib3==1.25
Based on closed urllib3/urllib3 issue #718 · PR/commit linked
Production note: This usually shows up under retries/timeouts. Treat it as a side-effect risk until you can verify behavior with a canary + real traffic.
@@ -130,8 +130,8 @@ def __init__(self, body='', headers=None, status=0, version=0, reason=None,
self.chunked = True
- # We certainly don't want to preload content when the response is chunked.
- if not self.chunked and preload_content and not self._body:
+ # If requested, preload the body.
http = HTTPConnectionPool("localhost", 4001, maxsize=2)
while True:
try:
resp = http.request("GET", "http://localhost:4001/v2/keys/calico/v1",
fields={"recursive": "true", "wait": "true",
"waitIndex": next_index},
timeout=5)
resp_body = loads(resp.data)
except ReadTimeoutError:
_log.info("Watch read timed out, restarting watch at index %s",
next_index)
continue
else:
...
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\nWhen NOT to use: This fix should not be used if the application relies on the previous behavior of not preloading chunked responses.\n\n
Why This Fix Works in Production
- Trigger: 2015-10-15 10:52:03,594 [INFO][46889/140035896854272] __main__ 64: About to call http.request...
- Mechanism: The connection handling for chunked responses was not correctly implemented, causing ReadTimeoutError
- Why the fix works: Resolves the issue by allowing chunked bodies to be preloaded correctly, addressing the ReadTimeoutError experienced during long-poll reads. (first fixed release: 1.25).
- If left unfixed, retry loops can amplify load and turn a small outage into a cascade (thundering herd).
Why This Breaks in Prod
- Shows up under Python 2.7 in real deployments (not just unit tests).
- The connection handling for chunked responses was not correctly implemented, causing ReadTimeoutError
- Surfaces as: 2015-10-15 10:52:03,594 [INFO][46889/140035896854272] __main__ 64: About to call http.request...
Proof / Evidence
- GitHub issue: #718
- Fix PR: https://github.com/urllib3/urllib3/pull/722
- First fixed release: 1.25
- 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.39
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“This is using normal Threads on Python 2.7, no gevent monkeypatching going on.”
“So just to clarify, when the 5s delay occurs on the _second_ call to request(), before either of those logs is emitted?”
“Hmm, I'm a little bit interested in where your ReadTimeoutError is coming from. My suspicion is that you should be getting some retries before that…”
“Adjusted my script to give better diags: Yes, there's a 5s delay after the second call to request. In case it helps, etcd pushes its…”
Failure Signature (Search String)
- 2015-10-15 10:52:03,594 [INFO][46889/140035896854272] __main__ 64: About to call http.request...
Error Message
Stack trace
Error Message
-------------
2015-10-15 10:52:03,594 [INFO][46889/140035896854272] __main__ 64: About to call http.request...
2015-10-15 10:52:03,596 [INFO][46889/140035896854272] urllib3.connectionpool 207: Starting new HTTP connection (1): localhost
2015-10-15 10:52:03,598 [DEBUG][46889/140035896854272] urllib3.connectionpool 387: "GET http://localhost:4001/v2/keys/calico/v1?waitIndex=7653237&recursive=true&wait=true HTTP/1.1" 200 None
2015-10-15 10:52:08,604 [ERROR][46889/140035896854272] __main__ 72: Watch read timed out, restarting watch at index 7653237
Traceback (most recent call last):
File "calico/felix/readetcd.py", line 69, in watch_etcd
resp_body = loads(resp.data)
File "/home/gulfstream/git/calico/env27/local/lib/python2.7/site-packages/urllib3/response.py", line 164, in data
return self.read(cache_content=True)
File "/home/gulfstream/git/calico/env27/local/lib/python2.7/site-packages/urllib3/response.py", line 292, in read
flush_decoder = True
File "/usr/lib/python2.7/contextlib.py", line 35, in __exit__
self.gen.throw(type, value, traceback)
File "/home/gulfstream/git/calico/env27/local/lib/python2.7/site-packages/urllib3/response.py", line 219, in _error_catcher
raise ReadTimeoutError(self._pool, None, 'Read timed out.')
ReadTimeoutError: HTTPConnectionPool(host='localhost', port=4001): Read timed out.
2015-10-15 10:52:08,606 [INFO][46889/140035896854272] __mai
... (truncated) ...
Minimal Reproduction
http = HTTPConnectionPool("localhost", 4001, maxsize=2)
while True:
try:
resp = http.request("GET", "http://localhost:4001/v2/keys/calico/v1",
fields={"recursive": "true", "wait": "true",
"waitIndex": next_index},
timeout=5)
resp_body = loads(resp.data)
except ReadTimeoutError:
_log.info("Watch read timed out, restarting watch at index %s",
next_index)
continue
else:
...
Environment
- Python: 2.7
- urllib3: 1.12
What Broke
Users experienced unexpected delays and ReadTimeoutError during long-poll reads.
Why It Broke
The connection handling for chunked responses was not correctly implemented, causing ReadTimeoutError
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
pip install urllib3==1.25
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/722
First fixed release: 1.25
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix should not be used if the application relies on the previous behavior of not preloading chunked responses.
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.
- Track RSS + object counts after deployments; alert on monotonic growth and GC pressure.
- Add a long-running test that repeats the failing call path and asserts stable memory.
Version Compatibility Table
| Version | Status |
|---|---|
| 1.25 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.