This repository was archived by the owner on Aug 19, 2025. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 127
This repository was archived by the owner on Aug 19, 2025. It is now read-only.
RuntimeError: Cannot call "receive" once a disconnect message has been received. #123
Copy link
Copy link
Closed
Description
My code:
class RoomChatWebsocket(WebSocketEndpoint):
encoding = "json"
@token.required
async def on_connect(self, websocket: WebSocket):
await websocket.accept()
user = websocket.aniko_user
room_id = websocket.query_params['room_id']
websocket.room_id = room_id
messages = red.lrange(f"room_chat_{room_id}", 0, -1)
if not messages:
red.expire(f"room_chat_{room_id}", red_expiry)
join_message = {
"user": {
"user_id": user.user_id,
"username": user.username,
"pfp": user.get_profile_pic(),
"tier": user.tier,
"room_id": room_id,
"is_system": 1
},
"message_data": {
"text": f"@{user.username} has joined the room!",
}
}
red.lpush(f"room_chat_{room_id}", orjson.dumps(join_message).decode('utf-8'))
messages = red.lrange(f"room_chat_{room_id}", 0, 100)
data = [orjson.loads(message) for message in messages]
# tell everyone someone joined
await broadcast.publish(channel=f"room_{room_id}", message=orjson.dumps(join_message).decode('utf-8'))
# send chat data to newly joined member
await websocket.send_json(data)
# handle subscriptions using anyio and broadcaster
async with anyio.create_task_group() as task_group:
# Receiver Task
async def receiver():
await self.on_message_received(websocket=websocket)
task_group.cancel_scope.cancel()
task_group.start_soon(receiver)
# Sender Task
await self.send_message_to_all(websocket)
async def send_message_to_all(self, websocket):
async with broadcast.subscribe(channel=f"room_{websocket.room_id}") as subscriber:
async for event in subscriber:
await websocket.send_json(orjson.loads(event.message))
async def on_message_received(self, websocket: WebSocket):
user = websocket.aniko_user
room_id = websocket.room_id
try:
async for data in websocket.iter_json():
if websocket.client_state != WebSocketState.CONNECTED:
return
chat_data = {
"user": {
"user_id": user.user_id,
"username": user.username,
"pfp": user.get_profile_pic(),
"tier": user.tier,
"room_id": room_id,
"is_system": 0
},
"message_data": {
"text": data['text'],
}
}
chat_data = orjson.dumps(chat_data).decode('utf-8')
red.lpush(f"room_chat_{room_id}", chat_data)
# Publish the message to the broadcast channel
await broadcast.publish(channel=f"room_{room_id}", message=chat_data)
except WebSocketDisconnect:
pass
async def on_disconnect(self, websocket: WebSocket, close_code: int):
user = websocket.aniko_user
room_id = websocket.room_id
# Notify others that the user has left
leave_message = {
"user": {
"user_id": user.user_id,
"username": user.username,
"pfp": user.get_profile_pic(),
"tier": user.tier,
"room_id": room_id,
"is_system": 1
},
"message_data": {
"text": f"@{user.username} has left the room :(",
}
}
leave_message = orjson.dumps(leave_message).decode('utf-8')
red.lpush(f"room_chat_{room_id}", leave_message)
# Publish the leave message to the broadcast channel
await broadcast.publish(channel=f"room_{room_id}", message=leave_message)
print(f"Disconnected: {websocket}")Stacktrace:
INFO: connection closed
Disconnected: <starlette.websockets.WebSocket object at 0x10a907550>
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/venv/lib/python3.11/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 254, in run_asgi
result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
await self.middleware_stack(scope, receive, send)
File "/venv/lib/python3.11/site-packages/starlette/middleware/errors.py", line 149, in __call__
await self.app(scope, receive, send)
File "/venv/lib/python3.11/site-packages/starlette/middleware/gzip.py", line 26, in __call__
await self.app(scope, receive, send)
File "/venv/lib/python3.11/site-packages/starlette/middleware/cors.py", line 76, in __call__
await self.app(scope, receive, send)
File "/venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
raise exc
File "/venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
await self.app(scope, receive, sender)
File "/venv/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
await route.handle(scope, receive, send)
File "/venv/lib/python3.11/site-packages/starlette/routing.py", line 341, in handle
await self.app(scope, receive, send)
File "/venv/lib/python3.11/site-packages/starlette/endpoints.py", line 89, in dispatch
raise exc
File "/venv/lib/python3.11/site-packages/starlette/endpoints.py", line 78, in dispatch
message = await websocket.receive()
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv/lib/python3.11/site-packages/starlette/websockets.py", line 56, in receive
raise RuntimeError(
RuntimeError: Cannot call "receive" once a disconnect message has been received.
Using:
starlette==0.26.1
uvicorn==0.21.1 & gunicorn==20.1.0 (tested using both)
Error happens when client disconnects. I am testing using insomnia.
I tried try except, checking for client state but to no avail. I hope this project is maintained still because this is the best bet I have in implementing a room chat system.
Metadata
Metadata
Assignees
Labels
No labels