Repository navigation
Pre-handshake disconnect is reported as duplicate websocket.accept #3052
Replies: 1 comment
|
Your suspected cause is right, and this looks like it's already fixed on master On 0.52.0, connection_lost() sets handshake_complete = True and nothing else send() then branches purely on that flag, with no disconnect check ahead of it: So a pre-handshake disconnect flips handshake_complete, the later On master, #3050 ("Handle connection loss during WebSocket write backpressure") Because that guard runs before any handshake-state branching, a pre-handshake I ran your reproduction unmodified against both: (Python 3.13, websockets 15.0.1, ws="websockets-sansio".) Two things that might still be worth doing, since #3050 was aimed at write
Happy to open a PR for the regression test if that'd be useful. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
When a peer disconnects after the WebSocket request is parsed but before the ASGI application accepts it, the
websockets-sansioimplementation marks the handshake as complete inconnection_lost(). A laterwebsocket.acceptfrom the application then enters the post-handshake branch and raises:RuntimeError: Expected ASGI message 'websocket.send' or 'websocket.close', but got 'websocket.accept'.This is reproducible with standalone Uvicorn; Gunicorn isn't involved. The same race also reproduces with
wsproto. The deprecatedwebsocketsimplementation doesn't log an application exception for this reproduction.Environment
ws="websockets-sansio"Minimal reproduction
Actual behavior
The ASGI application is logged as failing with the duplicate-accept
RuntimeError.Expected behavior
A peer disconnect before acceptance should be treated as a client disconnect, ideally by raising
ClientDisconnected(whichrun_asgi()already handles), rather than as an invalid ASGI message sequence.Suspected cause
connection_lost()unconditionally setshandshake_complete = True.send()then selects the post-handshake state based on that flag before accounting for the closed transport.Potential fixes might be to track connection loss separately or raise
ClientDisconnectedwhen the transport is already closing before validating a new ASGI send. A regression test coveringconnection_lost()beforewebsocket.acceptwould protect both SansIO and wsproto.All reactions