From 006ad8a8124f3d53ec96078297d5d152f013f9ca Mon Sep 17 00:00:00 2001 From: Shanu Date: Fri, 22 May 2026 16:30:19 +0530 Subject: [PATCH 1/2] fix(socketService): handle stale disconnected socket instances to ensure fresh connections --- app/src/services/socketService.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/services/socketService.ts b/app/src/services/socketService.ts index 57cde7af1..88e72b009 100644 --- a/app/src/services/socketService.ts +++ b/app/src/services/socketService.ts @@ -161,6 +161,13 @@ class SocketService { } else if (!this.socket.disconnected) { // Socket is connecting, wait for it return; + } else { + // Stale disconnected socket instance for the same token. + // Drop it so this connect attempt can create a fresh socket; + // otherwise the async stale-invocation guard below (`|| this.socket`) + // returns early and leaves connectivity stuck at "connecting". + this.socket = null; + this.mcpTransport = null; } } From 301bb33bcba7cd5820d3080f664686a2b6cddc98 Mon Sep 17 00:00:00 2001 From: Shanu Date: Fri, 22 May 2026 16:30:36 +0530 Subject: [PATCH 2/2] test(socketService): add test for reconnecting stale disconnected sockets to ensure fresh connections --- .../services/__tests__/socketService.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/src/services/__tests__/socketService.test.ts b/app/src/services/__tests__/socketService.test.ts index 462356a5e..352ae0126 100644 --- a/app/src/services/__tests__/socketService.test.ts +++ b/app/src/services/__tests__/socketService.test.ts @@ -275,6 +275,25 @@ describe('socketService — resolveCoreSocketBaseUrl uses getCoreRpcUrl', () => ); expect(latestSocket.once).not.toHaveBeenCalledWith('queued-on-event', expect.any(Function)); }); + + it('reconnects when a stale disconnected socket exists for the same token', async () => { + const { io } = await import('socket.io-client'); + const ioMock = vi.mocked(io); + ioMock.mockClear(); + + hoisted.getCoreRpcUrlMock.mockResolvedValue('http://127.0.0.1:7788/rpc'); + + const { socketService } = await import('../socketService'); + socketService.disconnect(); + + socketService.connect('mock-jwt-stale-socket'); + await pollUntil(() => expect(ioMock).toHaveBeenCalledTimes(1)); + + // Same token, previous socket is disconnected=true in our mock. + // We should still create a fresh socket instead of returning early. + socketService.connect('mock-jwt-stale-socket'); + await pollUntil(() => expect(ioMock).toHaveBeenCalledTimes(2)); + }); }); describe('socketService — connectivity dispatch on socket events (lines 164, 212, 230, 237, 240)', () => {