From dd9bb857640dd871932041d0f28a8f6b19d05bb3 Mon Sep 17 00:00:00 2001 From: Yash Singh Date: Tue, 16 Jun 2026 00:28:17 +0530 Subject: [PATCH] fix(web,react-native): stop realtime heartbeat from stacking and crashing on closed socket The realtime client cleared its heartbeat with clearTimeout even though the handle comes from setInterval, so the interval was never cancelled and a new one stacked on every reconnect. The interval body also called socket.send() with no readyState check, which throws INVALID_STATE_ERROR when the socket is CLOSING/CLOSED during a reconnect (e.g. after a background + network-drop + foreground cycle). Switch to clearInterval and only send the ping when the socket is OPEN, in both the web and react-native client templates. --- templates/react-native/src/client.ts.twig | 10 ++++++---- templates/web/src/client.ts.twig | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/templates/react-native/src/client.ts.twig b/templates/react-native/src/client.ts.twig index 5f2b423b0d..fefdaba931 100644 --- a/templates/react-native/src/client.ts.twig +++ b/templates/react-native/src/client.ts.twig @@ -309,13 +309,15 @@ class Client { }, createHeartbeat: () => { if (this.realtime.heartbeat) { - clearTimeout(this.realtime.heartbeat); + clearInterval(this.realtime.heartbeat); } this.realtime.heartbeat = window?.setInterval(() => { - this.realtime.socket?.send(JSONbig.stringify({ - type: 'ping' - })); + if (this.realtime.socket?.readyState === WebSocket.OPEN) { + this.realtime.socket.send(JSONbig.stringify({ + type: 'ping' + })); + } }, 20_000); }, createSocket: () => { diff --git a/templates/web/src/client.ts.twig b/templates/web/src/client.ts.twig index d28c114aba..b0a0880b43 100644 --- a/templates/web/src/client.ts.twig +++ b/templates/web/src/client.ts.twig @@ -504,13 +504,15 @@ class Client { }, createHeartbeat: () => { if (this.realtime.heartbeat) { - clearTimeout(this.realtime.heartbeat); + clearInterval(this.realtime.heartbeat); } this.realtime.heartbeat = window?.setInterval(() => { - this.realtime.socket?.send(JSONbig.stringify({ - type: 'ping' - })); + if (this.realtime.socket?.readyState === WebSocket.OPEN) { + this.realtime.socket.send(JSONbig.stringify({ + type: 'ping' + })); + } }, 20_000); }, createSocket: () => {