websockets
websockets copied to clipboard
AttributeError: 'WebSocketProtocol' object has no attribute 'transfer_data_task'
When the client disconnects just after setting self.application_state = WebSocketState.CONNECTED
, subsequent ws.close()
will fail at .venv/lib/python3.11/site-packages/websockets/legacy/protocol.py:784
as transfer_data_task
will not be set up.
I suppose it should be guarded by if hasattr(self, "transfer_data_task"):
as it is in other places.
Thank you for your report.
It shouldn't be possible to reach this line in close()
before connection_open()
completes successfully. I'd like to understand why this happens and, if applicable, to fix the root cause rather than just patch the symptom.
Could you provide a bit more details, please?
- Can you confirm that you talking about a client implemented with websockets?
- Can you provide the full stack trace? Ideally, debug logs too? (See https://websockets.readthedocs.io/en/stable/topics/logging.html#configure-logging)
- Do you have a minimum example that produces this behavior?
+1 here:
2024-05-01 10:11:59,863 - ERROR - Task-368 - uvicorn/protocols/websockets/websockets_impl.py - run_asgi - Exception in ASGI application
Traceback (most recent call last):
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 240, in run_asgi
result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 69, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
await super().__call__(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 151, in __call__
await self.app(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 77, in __call__
await self.app(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 756, in __call__
await self.middleware_stack(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 776, in app
await route.handle(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 373, in handle
await self.app(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 96, in app
await wrap_app_handling_exceptions(app, session)(scope, receive, send)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 94, in app
await func(session)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 348, in app
await dependant.call(**values)
File "/data/myapp-yocto/myapp-appl/myapp-backend/src/myapp_backend/api_v1/routes/device.py", line 68, in websocket_endpoint # this is my application
async with ws_connection(ws, serial, timeout=1) as messages:
File "/usr/lib64/python3.12/contextlib.py", line 217, in __aexit__
await anext(self.gen)
File "/data/myapp-yocto/myapp-appl/myapp-backend/src/myapp_backend/api_v1/routes/websocket.py", line 80, in ws_connection
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/websockets.py", line 203, in close
await self.send(
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/websockets.py", line 97, in send
await self._send(message)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 50, in sender
await send(message)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 337, in asgi_send
await self.close(code, reason)
File "/data/myapp-yocto/myapp-appl/myapp-backend/.venv/lib64/python3.12/site-packages/websockets/legacy/protocol.py", line 788, in close
await self.transfer_data_task
^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'WebSocketProtocol' object has no attribute 'transfer_data_task'. Did you mean: 'transfer_data_exc'?
The issue occured while doing some evil concurrent load/abort/reload clicking of a Javascript/Vue.js-based page connecting to my websocket using the vueuse useWebsocket
composable. See https://vueuse.org/core/useWebSocket/ for details of the client.
This has to do with how uvicorn embeds websockets. It was integrated before I provided a good API for that use case — namely the Sans-I/O implementation. It's done by using private APIs and it doesn't respect perfectly the logic & invariants that websockets relies on. This is hard and not very valuable to fix.
Now the good API for embedding exists and it should be adopted in uvicorn: https://github.com/encode/uvicorn/issues/1908
That's the proper fix really.
PS: I don't know if that's the explanation of the first report; I never got details on that one. It's the explanation of the second report, where I have a stack trace.