Jump to solution
Verify

The Fix

Upgrade to version 0.13.1 or later.

Based on closed Kludex/uvicorn issue #244 · 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.

Jump to Verify Open PR/Commit
@@ -216,6 +216,7 @@ async def asgi_send(self, message): elif message_type == "websocket.close": code = message.get("code", 1000) + self.close_code = code # for WebSocketServerProtocol await self.close(code) self.closed_event.set()
repro.py
class Application: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): if self.scope["type"] != "websocket": return while True: # what for the 'websocket.connect' event. await receive() # Close the connection await send({'type': 'websocket.close'})
verify
Re-run: uvicorn asgi:Application --ws websockets
fix.md
Option A — Upgrade to fixed release\nUpgrade to version 0.13.1 or later.\nWhen NOT to use: This fix should not be applied if the application logic requires immediate connection closure.\n\nOption C — Workaround\nfor this? I can post more info about what I'm seeing, but it looks like the connection closes and things are fine browser side, but if I call .close() in the .connect() function I end up with type errors in the logs. Having stack traces in the logs is obviously not ideal, but it would help to clarify that's the extent of the issue...\nWhen NOT to use: This fix should not be applied if the application logic requires immediate connection closure.\n\n

Why This Fix Works in Production

  • Trigger: result = await asgi(self.asgi_receive, self.asgi_send)
  • Mechanism: The TypeError occurs when attempting to receive data from a closed WebSocket connection
  • Why the fix works: Fix failures when ASGI app rejects a connection during handshake, addressing TypeError when connection is closed. (first fixed release: 0.13.1).

Why This Breaks in Prod

  • Shows up under Python 3.7 in real deployments (not just unit tests).
  • The TypeError occurs when attempting to receive data from a closed WebSocket connection
  • Surfaces as: Traceback (most recent call last):

Proof / Evidence

  • GitHub issue: #244
  • Fix PR: https://github.com/kludex/uvicorn/pull/704
  • First fixed release: 0.13.1
  • 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.27

Discussion

High-signal excerpts from the issue thread (symptoms, repros, edge-cases).

“> The thing that would help most with progressing this would be to try to replicate the issue in a plain ASGI application, or in…”
@matteing · 2019-10-22 · repro detail · source
“> If anyone's able to reduce the issue like that then I'd be very happy to dig into it”
@adamhooper · 2020-09-08 · source
“Here is a minimal example without using django: To test it, you have to start it with an asgi server an open a websocket connection”
@ostcar · 2019-04-13 · repro detail · source
“The thing that would help most with progressing this would be to try to replicate the issue in a plain ASGI application, or in Starlette,…”
@lovelydinosaur · 2019-10-22 · repro detail · source

Failure Signature (Search String)

  • result = await asgi(self.asgi_receive, self.asgi_send)

Error Message

Stack trace
error.txt
Error Message ------------- Traceback (most recent call last): File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 140, in run_asgi result = await asgi(self.asgi_receive, self.asgi_send) File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/channels/sessions.py", line 179, in __call__ return await self.inner(receive, self.send) File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/channels/middleware.py", line 41, in coroutine_call await inner_instance(receive, send) File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/channels/consumer.py", line 59, in __call__ [receive, self.channel_receive], self.dispatch File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/channels/utils.py", line 59, in await_many_dispatch await task File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/channels/utils.py", line 51, in await_many_dispatch result = task.result() File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 222, in asgi_receive data = await self.recv() File "/home/ossi/src/channels-examples/.venv/lib/python3.7/site-packages/websockets/protocol.py", line 419, in recv return_when=asyncio.FIRST_COMPLETED, File "/usr/lib/py ... (truncated) ...
Stack trace
error.txt
Error Message ------------- TypeError: An asyncio.Future, a coroutine or an awaitable is required at ensure_future (/usr/local/lib/python3.6/asyncio/tasks.py:526) at <setcomp> (/usr/local/lib/python3.6/asyncio/tasks.py:311) at wait (/usr/local/lib/python3.6/asyncio/tasks.py:311) at recv (/usr/local/lib/python3.6/site-packages/websockets/protocol.py:419) at asgi_receive (/usr/local/lib/python3.6/site-packages/uvicorn/protocols/websockets/websockets_impl.py:227) at await_many_dispatch (/usr/local/lib/python3.6/site-packages/channels/utils.py:51) at await_many_dispatch (/usr/local/lib/python3.6/site-packages/channels/utils.py:59) at __call__ (/usr/local/lib/python3.6/site-packages/channels/consumer.py:59) at run_asgi (/usr/local/lib/python3.6/site-packages/uvicorn/protocols/websockets/websockets_impl.py:147)
Stack trace
error.txt
Error Message ------------- $ uvicorn asgi:Application --ws websockets INFO: Started server process [23052] INFO: Waiting for application startup. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: ('127.0.0.1', 58172) - "WebSocket /" 403 ERROR: Exception in ASGI application Traceback (most recent call last): File "/home/ossi/src/uvicorn_test/venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 147, in run_asgi result = await asgi(self.asgi_receive, self.asgi_send) File "./asgi.py", line 12, in __call__ await receive() File "/home/ossi/src/uvicorn_test/venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 227, in asgi_receive data = await self.recv() File "/home/ossi/src/uvicorn_test/venv/lib/python3.7/site-packages/websockets/protocol.py", line 419, in recv return_when=asyncio.FIRST_COMPLETED, File "/usr/lib/python3.7/asyncio/tasks.py", line 361, in wait fs = {ensure_future(f, loop=loop) for f in set(fs)} File "/usr/lib/python3.7/asyncio/tasks.py", line 361, in <setcomp> fs = {ensure_future(f, loop=loop) for f in set(fs)} File "/usr/lib/python3.7/asyncio/tasks.py", line 592, in ensure_future raise TypeError('An asyncio.Future, a coroutine or an awaitable is ' TypeError: An asyncio.Future, a coroutine or an awaitable is required ^CINFO: Shutting down INFO: Waiting for app ... (truncated) ...

Minimal Reproduction

repro.py
class Application: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): if self.scope["type"] != "websocket": return while True: # what for the 'websocket.connect' event. await receive() # Close the connection await send({'type': 'websocket.close'})

Environment

  • Python: 3.7

What Broke

Uvicorn raises TypeError during WebSocket connection closure, leading to log clutter.

Why It Broke

The TypeError occurs when attempting to receive data from a closed WebSocket connection

Fix Options (Details)

Option A — Upgrade to fixed release Safe default (recommended)

Upgrade to version 0.13.1 or later.

When NOT to use: This fix should not be applied if the application logic requires immediate connection closure.

Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.

Option C — Workaround Temporary workaround

for this? I can post more info about what I'm seeing, but it looks like the connection closes and things are fine browser side, but if I call .close() in the .connect() function I end up with type errors in the logs. Having stack traces in the logs is obviously not ideal, but it would help to clarify that's the extent of the issue...

When NOT to use: This fix should not be applied if the application logic requires immediate connection closure.

Use only if you cannot change versions today. Treat this as a stopgap and remove once upgraded.

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.
Show example snippet (optional)
onceonly.py
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)

See OnceOnly SDK

When NOT to use: 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.

Fix reference: https://github.com/kludex/uvicorn/pull/704

First fixed release: 0.13.1

Last verified: 2026-02-09. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • This fix should not be applied if the application logic requires immediate connection closure.
  • 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

verify
Re-run: uvicorn asgi:Application --ws websockets

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

VersionStatus
0.13.1 Fixed

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.