The Fix
Upgrade to version 0.13.3 or later.
Based on closed Kludex/uvicorn issue #920 · PR/commit linked
@@ -135,6 +135,9 @@ def send_500_response(self):
]
self.transport.write(b"".join(content))
+ # Allow handler task to terminate cleanly, as websockets doesn't cancel it by
+ # itself (see https://github.com/encode/uvicorn/issues/920)
+ self.handshake_started_event.set()
$ pytest -k test_missing_handshake
====================================================== test session starts =======================================================
platform darwin -- Python 3.9.0, pytest-6.1.1, py-1.9.0, pluggy-0.13.1
rootdir: /Users/florimond/Developer/python-projects/uvicorn, configfile: setup.cfg
plugins: mock-3.3.1, asyncio-0.14.0
collected 213 items / 211 deselected / 2 selected
tests/protocols/test_websocket.py .. [100%]
=============================================== 2 passed, 211 deselected in 2.30s ================================================
Task was destroyed but it is pending!
task: <Task pending name='Task-11' coro=<WebSocketServerProtocol.handler() done, defined at /Users/florimond/Developer/python-projects/uvicorn/venv/lib/python3.9/site-packages/websockets/server.py:118> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10a7e1760>()]>>
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.13.3 or later.\nWhen NOT to use: Do not apply this fix if the websocket implementation does not handle handshake failures.\n\n
Why This Fix Works in Production
- Trigger: Websockets implementation does not clean properly tasks if handshake fails
- Mechanism: Handshake failures in the websockets implementation leave tasks pending
- Why the fix works: Fixes the websockets implementation where handshake failures leave tasks pending. (first fixed release: 0.13.3).
Why This Breaks in Prod
- Shows up under Python 3.9.0 in real deployments (not just unit tests).
- Handshake failures in the websockets implementation leave tasks pending
- Production symptom (often without a traceback): Websockets implementation does not clean properly tasks if handshake fails
Proof / Evidence
- GitHub issue: #920
- Fix PR: https://github.com/kludex/uvicorn/pull/921
- First fixed release: 0.13.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.42
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“this is a good explanation of what we see in those 2 tests and that causes flaky tests: FWIW there's an asyncio warning when I try to run test_send_before_handshake or test_missing_handshake in isolation. Doesn't show if I run either along”
Failure Signature (Search String)
- Websockets implementation does not clean properly tasks if handshake fails
Copy-friendly signature
Failure Signature
-----------------
Websockets implementation does not clean properly tasks if handshake fails
$ pytest -k test_missing_handshake
Error Message
Signature-only (no traceback captured)
Error Message
-------------
Websockets implementation does not clean properly tasks if handshake fails
$ pytest -k test_missing_handshake
Minimal Reproduction
$ pytest -k test_missing_handshake
====================================================== test session starts =======================================================
platform darwin -- Python 3.9.0, pytest-6.1.1, py-1.9.0, pluggy-0.13.1
rootdir: /Users/florimond/Developer/python-projects/uvicorn, configfile: setup.cfg
plugins: mock-3.3.1, asyncio-0.14.0
collected 213 items / 211 deselected / 2 selected
tests/protocols/test_websocket.py .. [100%]
=============================================== 2 passed, 211 deselected in 2.30s ================================================
Task was destroyed but it is pending!
task: <Task pending name='Task-11' coro=<WebSocketServerProtocol.handler() done, defined at /Users/florimond/Developer/python-projects/uvicorn/venv/lib/python3.9/site-packages/websockets/server.py:118> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10a7e1760>()]>>
Environment
- Python: 3.9.0
What Broke
Flaky tests due to pending tasks causing warnings and inconsistent test results.
Why It Broke
Handshake failures in the websockets implementation leave tasks pending
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.13.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/921
First fixed release: 0.13.3
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- Do not apply this fix if the websocket implementation does not handle handshake failures.
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 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 |
|---|---|
| 0.13.3 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.