From 47cad606f4db4cb08eb57d2b02c2fa590d90a0f6 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 17 Jun 2026 14:38:34 +0200 Subject: [PATCH] fix(gateway): clean pending request when send fails --- packages/gateway-client/src/client.ts | 9 +++++++- .../src/client.watchdog.test.ts | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/gateway-client/src/client.ts b/packages/gateway-client/src/client.ts index 2aa0dfce9b4f..55273f0cf26b 100644 --- a/packages/gateway-client/src/client.ts +++ b/packages/gateway-client/src/client.ts @@ -1599,7 +1599,14 @@ export class GatewayClient { }); signal?.addEventListener("abort", abortHandler, { once: true }); }); - this.ws.send(JSON.stringify(frame)); + try { + this.ws.send(JSON.stringify(frame)); + } catch (error) { + const pending = this.pending.get(id); + this.pending.delete(id); + pending?.cleanup?.(); + throw error; + } return p; } } diff --git a/packages/gateway-client/src/client.watchdog.test.ts b/packages/gateway-client/src/client.watchdog.test.ts index 4d3b0e8a642a..2e565b2b7029 100644 --- a/packages/gateway-client/src/client.watchdog.test.ts +++ b/packages/gateway-client/src/client.watchdog.test.ts @@ -317,6 +317,27 @@ describe("GatewayClient", () => { } }); + test("cleans pending request state when websocket send throws", async () => { + const client = new GatewayClient({ + requestTimeoutMs: 25, + }); + const sendError = new Error("synthetic send failure"); + ( + client as unknown as { + ws: WebSocket | { readyState: number; send: () => void; close: () => void }; + } + ).ws = { + readyState: WebSocket.OPEN, + send: vi.fn(() => { + throw sendError; + }), + close: vi.fn(), + }; + + await expect(client.request("status")).rejects.toThrow("synthetic send failure"); + expect(getPendingCount(client)).toBe(0); + }); + test("does not auto-timeout expectFinal requests", async () => { vi.useFakeTimers(); try {