Skip to content

Commit 1f50bb1

Browse files
authored
Properly handle websocket transport errors and recovery (#1897)
1 parent cf4dcf2 commit 1f50bb1

File tree

4 files changed

+19
-4
lines changed

4 files changed

+19
-4
lines changed

changes/1897.bugfix.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Properly handle websocket transport errors and recover
2+
- Additionally, errors will now include additional information

hikari/errors.py

+9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"InternalServerError",
4242
"ShardCloseCode",
4343
"GatewayConnectionError",
44+
"GatewayTransportError",
4445
"GatewayServerClosedConnectionError",
4546
"GatewayError",
4647
"MissingIntentWarning",
@@ -176,6 +177,14 @@ def is_standard(self) -> bool:
176177
return (self.value // 1000) == 1
177178

178179

180+
@attrs.define(auto_exc=True, repr=False, slots=False)
181+
class GatewayTransportError(GatewayError):
182+
"""An exception thrown if an issue occurs at the transport layer."""
183+
184+
def __str__(self) -> str:
185+
return f"Gateway transport error: {self.reason!r}"
186+
187+
179188
@attrs.define(auto_exc=True, repr=False, slots=False)
180189
class GatewayConnectionError(GatewayError):
181190
"""An exception thrown if a connection issue occurs."""

hikari/impl/shard.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ async def send_json(self, data: data_binding.JSONObject) -> None:
210210

211211
def _handle_other_message(self, message: aiohttp.WSMessage, /) -> typing.NoReturn:
212212
if message.type == aiohttp.WSMsgType.TEXT:
213-
raise errors.GatewayError("Unexpected message type received TEXT, expected BINARY")
213+
raise errors.GatewayTransportError("Unexpected message type received TEXT, expected BINARY")
214214

215215
if message.type == aiohttp.WSMsgType.BINARY:
216-
raise errors.GatewayError("Unexpected message type received BINARY, expected TEXT")
216+
raise errors.GatewayTransportError("Unexpected message type received BINARY, expected TEXT")
217217

218218
if message.type == aiohttp.WSMsgType.CLOSE:
219219
close_code = int(message.data)
@@ -229,7 +229,8 @@ def _handle_other_message(self, message: aiohttp.WSMessage, /) -> typing.NoRetur
229229
raise errors.GatewayConnectionError("Socket has closed")
230230

231231
# Assume exception for now.
232-
raise errors.GatewayError("Unexpected websocket exception from gateway") from self._ws.exception()
232+
reason = f"{message.data!r} [extra={message.extra!r}, type={message.type}]"
233+
raise errors.GatewayTransportError(reason) from self._ws.exception()
233234

234235
async def _receive_and_check_text(self) -> str:
235236
message = await self._ws.receive()
@@ -920,6 +921,9 @@ async def _keep_alive(self) -> None:
920921
except errors.GatewayConnectionError as ex:
921922
self._logger.warning("failed to communicate with server, reason was: %r. Will retry shortly", ex.reason)
922923

924+
except errors.GatewayTransportError as ex:
925+
self._logger.error("encountered transport error. Will try to reconnect shorty", exc_info=ex)
926+
923927
except errors.GatewayServerClosedConnectionError as ex:
924928
if not ex.can_reconnect:
925929
self._logger.info(

tests/hikari/impl/test_shard.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def test__handle_other_message_when_message_type_is_unknown(self, transport_impl
269269
exception = Exception("some error")
270270
transport_impl._ws.exception = mock.Mock(return_value=exception)
271271

272-
with pytest.raises(errors.GatewayError, match="Unexpected websocket exception from gateway") as exc_info:
272+
with pytest.raises(errors.GatewayTransportError) as exc_info:
273273
transport_impl._handle_other_message(stub_response)
274274

275275
assert exc_info.value.__cause__ is exception

0 commit comments

Comments
 (0)