From eb948d5820a723ced7aa207ab8979ab9669dbde5 Mon Sep 17 00:00:00 2001 From: paisley <8197966+su8su@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:51:30 +0800 Subject: [PATCH] fix(gateway): restore WebSocket heartbeat ping on Windows (#722) --- electron/gateway/manager.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/electron/gateway/manager.ts b/electron/gateway/manager.ts index ad171a64a..577e7ad37 100644 --- a/electron/gateway/manager.ts +++ b/electron/gateway/manager.ts @@ -787,14 +787,7 @@ export class GatewayManager extends EventEmitter { port, connectedAt: Date.now(), }); - // On Windows, skip WebSocket heartbeat ping to avoid cascading failures: - // heartbeat timeout → terminate socket → reconnect → port conflict - // (old process holds port due to TCP TIME_WAIT) → ~2 min downtime. - // Gateway is a local child process; actual crashes are caught by the - // process exit handler, and graceful restarts use code=1012 close frames. - if (process.platform !== 'win32') { - this.startPing(); - } + this.startPing(); }, onMessage: (message) => { this.handleMessage(message); @@ -900,6 +893,14 @@ export class GatewayManager extends EventEmitter { } catch (error) { logger.warn('Failed to terminate stale Gateway socket after heartbeat timeout:', error); } + + // On Windows, onCloseAfterHandshake intentionally skips scheduleReconnect() + // to avoid double-reconnect races with the process exit handler. However, + // a heartbeat timeout means the socket is stale while the process may still + // be alive (no exit event), so we must explicitly trigger reconnect here. + if (process.platform === 'win32') { + this.scheduleReconnect(); + } }, }); }