From 66f2f41ad184e33e24c2f78e01ee56d7c782c740 Mon Sep 17 00:00:00 2001 From: Oliver Ni Date: Fri, 8 May 2026 01:52:43 -0700 Subject: [PATCH] Fix heartbeat latency race between keepalive and event loop threads Set _last_send on the event loop thread before sending so ack() can't read a stale value from the previous heartbeat cycle. --- discord/gateway.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/discord/gateway.py b/discord/gateway.py index 45034d01a..47b485c86 100644 --- a/discord/gateway.py +++ b/discord/gateway.py @@ -173,8 +173,7 @@ class KeepAliveHandler(threading.Thread): data = self.get_payload() _log.debug(self.msg, self.shard_id, data['d']) - coro = self.ws.send_heartbeat(data) - f = asyncio.run_coroutine_threadsafe(coro, loop=self.ws.loop) + f = asyncio.run_coroutine_threadsafe(self._send_heartbeat(data), loop=self.ws.loop) try: # block until sending is complete total = 0 @@ -195,8 +194,6 @@ class KeepAliveHandler(threading.Thread): except Exception: self.stop() - else: - self._last_send = time.perf_counter() def get_payload(self) -> Dict[str, Any]: return { @@ -214,6 +211,10 @@ class KeepAliveHandler(threading.Thread): self._last_send = time.perf_counter() return self.get_payload() + async def _send_heartbeat(self, data: Any) -> None: + self._last_send = time.perf_counter() + await self.ws.send_heartbeat(data) + def ack(self) -> None: ack_time = time.perf_counter() self._last_ack = ack_time