The Fix
Upgrade to version 0.12.3 or later.
Based on closed Kludex/uvicorn issue #847 · PR/commit linked
Production note: This tends to surface only under concurrency. Reproduce with load tests and watch for lock contention/cancellation paths.
@@ -113,7 +113,6 @@ def __init__(self, config, server_state, _loop=None):
self.headers = None
self.cycle = None
- self.message_event = asyncio.Event()
# Protocol interface
from quart import Quart, request
app = Quart(__name__)
@app.route('/', methods=['POST'])
async def hello():
data = await request.get_data()
return data
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.12.3 or later.\nWhen NOT to use: This fix is not applicable if using non-async frameworks or incompatible ASGI servers.\n\n
Why This Fix Works in Production
- Trigger: Requests hang when running quart with uvicorn
- Mechanism: Race condition in handling keep-alive connections causes requests to hang
- Why the fix works: Fix race condition that leads Quart to hang with uvicorn, addressing issues with keep-alive connections. (first fixed release: 0.12.3).
- 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.7.9 in real deployments (not just unit tests).
- Race condition in handling keep-alive connections causes requests to hang
- Production symptom (often without a traceback): Requests hang when running quart with uvicorn
Proof / Evidence
- GitHub issue: #847
- Fix PR: https://github.com/kludex/uvicorn/pull/848
- First fixed release: 0.12.3
- 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.58
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“Hey @euri10 , thank you so much for taking the effort into the issue! I've isolated a minimum repro asgi app, which is mostly stolen…”
“@euri10 If you add a break into handle_messages function, the issue will go away”
“sorry can't reproduce with this app in fact this app seems to behave quite like Starlette and the difference I pointed out in #848 is…”
“@euri10 Could you increase the duration time and connections of this wrk test? 1 minute and 512 connections will be enough.”
Failure Signature (Search String)
- Requests hang when running quart with uvicorn
- Meanwhile, if you change the `return data` to `return data, 200, {'Connection': 'close'}` which means disabled reusing socket, the QPS will go up. It also means you cannot take
Copy-friendly signature
Failure Signature
-----------------
Requests hang when running quart with uvicorn
Meanwhile, if you change the `return data` to `return data, 200, {'Connection': 'close'}` which means disabled reusing socket, the QPS will go up. It also means you cannot take advantage of keeping alive connections.
Error Message
Signature-only (no traceback captured)
Error Message
-------------
Requests hang when running quart with uvicorn
Meanwhile, if you change the `return data` to `return data, 200, {'Connection': 'close'}` which means disabled reusing socket, the QPS will go up. It also means you cannot take advantage of keeping alive connections.
Minimal Reproduction
from quart import Quart, request
app = Quart(__name__)
@app.route('/', methods=['POST'])
async def hello():
data = await request.get_data()
return data
Environment
- Python: 3.7.9
What Broke
Requests hang after the first batch, leading to timeouts and low QPS.
Why It Broke
Race condition in handling keep-alive connections causes requests to hang
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.12.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/kludex/uvicorn/pull/848
First fixed release: 0.12.3
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix is not applicable if using non-async frameworks or incompatible ASGI servers.
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 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.
- Make timeouts explicit and test them (unit + integration) to avoid silent behavior changes.
- Instrument retries (attempt count + reason) and alert on spikes to catch dependency slowdowns.
Version Compatibility Table
| Version | Status |
|---|---|
| 0.12.3 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.