Summary
The QQBot adapter can stop reconnecting after a failed reconnect attempt if the previous websocket object remains present but closed.
What happens
After a QQBot websocket disconnect, the reconnect path may fail transiently while fetching a fresh gateway URL, for example during a DNS or network outage:
WebSocket error: WebSocket closed
Reconnecting in 2s (attempt 1)...
Reconnect failed: Failed to get QQ Bot gateway URL: [Errno 8] nodename nor servname provided, or not known
After that, the adapter may remain disconnected indefinitely. Subsequent sends only report that the platform is not connected, but no further reconnect attempts are made.
Root cause
QQAdapter._read_events() only raises when self._ws is missing. If self._ws is still assigned but already closed, the read loop condition is false and the method returns normally:
if not self._ws:
raise RuntimeError("WebSocket not connected")
while self._running and self._ws and not self._ws.closed:
...
That normal return makes _listen_loop() treat the read as a clean exit, reset reconnect bookkeeping, and skip the exception path that would call _reconnect() again.
Expected behavior
A closed websocket should be treated as a disconnected state. _read_events() should raise so _listen_loop() continues through the existing reconnect path.
Reproduction
A minimal reproduction is to set adapter._ws to a websocket-like object where closed == True and call _read_events() while the adapter is running. The method currently returns without raising, which prevents the listener from continuing reconnect handling.
Fix
Raise RuntimeError("WebSocket closed") when _read_events() is entered with an already-closed websocket object. Add a regression test to make sure closed-but-present websocket objects cannot be treated as a clean read termination.
Summary
The QQBot adapter can stop reconnecting after a failed reconnect attempt if the previous websocket object remains present but closed.
What happens
After a QQBot websocket disconnect, the reconnect path may fail transiently while fetching a fresh gateway URL, for example during a DNS or network outage:
After that, the adapter may remain disconnected indefinitely. Subsequent sends only report that the platform is not connected, but no further reconnect attempts are made.
Root cause
QQAdapter._read_events()only raises whenself._wsis missing. Ifself._wsis still assigned but already closed, the read loop condition is false and the method returns normally:That normal return makes
_listen_loop()treat the read as a clean exit, reset reconnect bookkeeping, and skip the exception path that would call_reconnect()again.Expected behavior
A closed websocket should be treated as a disconnected state.
_read_events()should raise so_listen_loop()continues through the existing reconnect path.Reproduction
A minimal reproduction is to set
adapter._wsto a websocket-like object whereclosed == Trueand call_read_events()while the adapter is running. The method currently returns without raising, which prevents the listener from continuing reconnect handling.Fix
Raise
RuntimeError("WebSocket closed")when_read_events()is entered with an already-closed websocket object. Add a regression test to make sure closed-but-present websocket objects cannot be treated as a clean read termination.