The Fix
pip install urllib3==2.4.0
Based on closed urllib3/urllib3 issue #3567 · PR/commit linked
Production note: Watch p95/p99 latency and retry volume; timeouts can turn into retry storms and duplicate side-effects.
@@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
+Updated exceptions to save and restore more properties
+during the pickle/serialization process.
\ No newline at end of file
>> import platform
>> import ssl
>> import urllib3
>>
>> print("OS", platform.platform())
print("Python", platform.python_version())
print(ssl.OPENSSL_VERSION)
print("urllib3", urllib3.__version__)
OS macOS-15.3.1-x86_64-i386-64bit
>> print("Python", platform.python_version())
Python 3.10.14
>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.1.1w 11 Sep 2023
>> print("urllib3", urllib3.__version__)
urllib3 2.3.0
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==2.4.0\nWhen NOT to use: This fix is not suitable if maintaining original exception behavior is critical.\n\n
Why This Fix Works in Production
- Trigger: E requests.exceptions.ReadTimeout: None: oh-no!
- Mechanism: ReadTimeoutError loses context during pickling in multiprocessing
- Why the fix works: Updated exceptions to save and restore more properties during the pickle/serialization process. (first fixed release: 2.4.0).
- 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.10.14 in real deployments (not just unit tests).
- ReadTimeoutError loses context during pickling in multiprocessing
- Surfaces as: E requests.exceptions.ReadTimeout: None: oh-no!
Proof / Evidence
- GitHub issue: #3567
- Fix PR: https://github.com/urllib3/urllib3/pull/3572
- First fixed release: 2.4.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.69
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“To give a tiny bit more context, my app is actually using requests, but it saves the urllib3 ReadTimeoutError then wraps it like so: https://github.com/psf/requests/blob/main/src/requests/adapters.py#L713”
“FYI, ex.url will be pickled https://github.com/urllib3/urllib3/blob/65d7b5fdc332f9f51aad2f119fae683eb080919d/src/urllib3/exceptions.py#L48-L50 I guess a passed message was not added to the pickled data because it was not retrievable from an…”
“https://github.com/urllib3/urllib3/blob/b20c8368ec02421ecc4e49d796cd5eff1e04f325/src/urllib3/exceptions.py#L29-L39 @csm10495 we can save str(self) instead of self._message in __reduce__”
“@illia-v, I came up with yet another way to better handle this. Filed: https://github.com/urllib3/urllib3/issues/3707. We can pick up over there. Thanks!”
Failure Signature (Search String)
- E requests.exceptions.ReadTimeout: None: oh-no!
Error Message
Stack trace
Error Message
-------------
E requests.exceptions.ReadTimeout: None: oh-no!
Minimal Reproduction
>> import platform
>> import ssl
>> import urllib3
>>
>> print("OS", platform.platform())
print("Python", platform.python_version())
print(ssl.OPENSSL_VERSION)
print("urllib3", urllib3.__version__)
OS macOS-15.3.1-x86_64-i386-64bit
>> print("Python", platform.python_version())
Python 3.10.14
>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.1.1w 11 Sep 2023
>> print("urllib3", urllib3.__version__)
urllib3 2.3.0
Environment
- Python: 3.10.14
- urllib3: 2.3.0
What Broke
Exceptions raised in multiprocessing lose important context, leading to confusion.
Why It Broke
ReadTimeoutError loses context during pickling in multiprocessing
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
pip install urllib3==2.4.0
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Option D — Guard side-effects with OnceOnly Guardrail for side-effects
Mitigate duplicate external side-effects under retries/timeouts/agent loops by gating the operation before calling external systems.
- Place OnceOnly between your code/agent and real side-effects (Stripe, emails, CRM, APIs).
- Use a stable key per side-effect (e.g., customer_id + action + idempotency_key).
- Fail-safe: configure fail-open vs fail-closed based on blast radius and spend risk.
- This does NOT fix data corruption; it only prevents duplicate side-effects.
Show example snippet (optional)
from onceonly import OnceOnly
import os
once = OnceOnly(api_key=os.environ["ONCEONLY_API_KEY"], fail_open=True)
# Stable idempotency key per real side-effect.
# Use a request id / job id / webhook delivery id / Stripe event id, etc.
event_id = "evt_..." # replace
key = f"stripe:webhook:{event_id}"
res = once.check_lock(key=key, ttl=3600)
if res.duplicate:
return {"status": "already_processed"}
# Safe to execute the side-effect exactly once.
handle_event(event_id)
Fix reference: https://github.com/urllib3/urllib3/pull/3572
First fixed release: 2.4.0
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix is not suitable if maintaining original exception behavior is critical.
- Do not use this to hide logic bugs or data corruption. Use it to block duplicate external side-effects and enforce tool permissions/spend caps.
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 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
| Version | Status |
|---|---|
| 2.4.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.