The Fix
Upgrade to version 0.7.3 or later.
Based on closed encode/httpx issue #382 · PR/commit linked
Production note: This tends to surface only under concurrency. Reproduce with load tests and watch for lock contention/cancellation paths.
@@ -81,6 +81,12 @@ async def read(
if should_raise:
raise ReadTimeout() from None
+ # FIX(py3.6): yield control back to the event loop to give it a chance
+ # to cancel `.read(n)` before we retry.
+ # This prevents concurrent `.read()` calls, which asyncio
>> import httpx
>> no_timeout = httpx.config.TimeoutConfig()
>> session = httpx.Client(timeout=no_timeout)
>> session.get("https://httpbin.org")
<Response [200 OK]>
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Upgrade to fixed release\nUpgrade to version 0.7.3 or later.\nWhen NOT to use: Do not use this fix if running on Python versions other than 3.6.\n\n
Why This Fix Works in Production
- Trigger: ERROR:__main__:Error
- Mechanism: Fixes a bug that occurred when using the HTTPX client without timeouts under Python 3.6, addressing a race condition in asyncio's read method.
- Why the fix works: Fixes a bug that occurred when using the HTTPX client without timeouts under Python 3.6, addressing a race condition in asyncio's read method. (first fixed release: 0.7.3).
- If left unfixed, failures can be intermittent under concurrency (hard to reproduce; shows up as sporadic 5xx/timeouts).
Why This Breaks in Prod
- Triggered by an upgrade/regression window: 3.6–3.7 breaks; 0.7.3 is the first fixed release.
- Shows up under Python 3.6 in real deployments (not just unit tests).
- Surfaces as: ERROR:__main__:Error
Proof / Evidence
- GitHub issue: #382
- Fix PR: https://github.com/encode/httpx/pull/383
- First fixed release: 0.7.3
- Affected versions: 3.6–3.7
- 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.38
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“@sethmlarson I debugged it a bit and it's a race condition between asyncio cancelling the previous .read() and us starting the new one in case…”
“Thanks for reporting this :) Could be that in 3.6 asyncio’s read() only allowed one coroutine to call it at a time, while that constraint…”
“I do not. Further testing giving no timeout value to httpx.Client i.e. or giving a concrete timeout value i.e. both proceed as expected, with no…”
“I did see something about using an asyncio lock in my searches... https://stackoverflow.com/a/25799871”
Failure Signature (Search String)
- ERROR:__main__:Error
Error Message
Stack trace
Error Message
-------------
ERROR:__main__:Error
Traceback (most recent call last):
File "/Users/primoz/PycharmProjects/orange3-imageanalytics/orangecontrib/imageanalytics/widgets/owimageembedding.py", line 283, in __set_results
embeddings = f.result()
File "/Users/primoz/miniconda3/lib/python3.7/concurrent/futures/_base.py", line 425, in result
return self.__get_result()
File "/Users/primoz/miniconda3/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/Users/primoz/miniconda3/lib/python3.7/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "/Users/primoz/PycharmProjects/orange3-imageanalytics/orangecontrib/imageanalytics/widgets/owimageembedding.py", line 235, in run_embedding
image_processed_callback=advance)
File "/Users/primoz/PycharmProjects/orange3-imageanalytics/orangecontrib/imageanalytics/image_embedder.py", line 109, in __call__
return self.from_table(*args, **kwargs)
File "/Users/primoz/PycharmProjects/orange3-imageanalytics/orangecontrib/imageanalytics/image_embedder.py", line 142, in from_table
file_paths_valid, image_processed_callback)
File "/Users/primoz/PycharmProjects/orange3-imageanalytics/orangecontrib/imageanalytics/server_embedder.py", line 75, in from_file_paths
paths, repeats_count, image_processed_callback))
File "/Users/primoz/miniconda3/li
... (truncated) ...
Minimal Reproduction
>> import httpx
>> no_timeout = httpx.config.TimeoutConfig()
>> session = httpx.Client(timeout=no_timeout)
>> session.get("https://httpbin.org")
<Response [200 OK]>
Environment
- Python: 3.6
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.7.3 or later.
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Fix reference: https://github.com/encode/httpx/pull/383
First fixed release: 0.7.3
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- Do not use this fix if running on Python versions other than 3.6.
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 |
|---|---|
| 3.6 | Broken |
| 3.7 | Broken |
| 0.7.3 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.