From 3405ad510aade7d0d2269a536e6ea97e16c128ec Mon Sep 17 00:00:00 2001 From: Mark Xian Date: Wed, 22 Jul 2026 08:04:38 +0800 Subject: [PATCH] fix(ui): clear stale pending ACP connection after terminal recovery failure (#10552) Co-authored-by: Claude Fable 5 --- .../src/acp/__tests__/acpConnection.test.ts | 23 +++++++++++++++++++ ui/desktop/src/acp/acpConnection.ts | 3 +++ 2 files changed, 26 insertions(+) diff --git a/ui/desktop/src/acp/__tests__/acpConnection.test.ts b/ui/desktop/src/acp/__tests__/acpConnection.test.ts index 85d5ade8e..8ef5a812b 100644 --- a/ui/desktop/src/acp/__tests__/acpConnection.test.ts +++ b/ui/desktop/src/acp/__tests__/acpConnection.test.ts @@ -136,6 +136,29 @@ describe('ACP connection ownership', () => { expect(getAcpUrl).toHaveBeenCalledOnce(); }); + it('does not cache terminal recovery failures across caller retries', async () => { + const { getAcpClient } = await import('../acpConnection'); + await getAcpClient(); + + const getAcpUrl = vi + .fn() + .mockRejectedValue( + new Error(`Error invoking remote method 'get-acp-url': ${GOOSE_SERVE_EXITED_USER_MESSAGE}`) + ); + window.electron.getAcpUrl = getAcpUrl; + sdk.instances[0].resolveClosed(); + await Promise.resolve(); + + const failedRecovery = expect(getAcpClient()).rejects.toThrow(GOOSE_SERVE_EXITED_USER_MESSAGE); + await vi.advanceTimersByTimeAsync(250); + await failedRecovery; + + await expect(getAcpClient()).rejects.toThrow(GOOSE_SERVE_EXITED_USER_MESSAGE); + + expect(getAcpUrl).toHaveBeenCalledTimes(2); + expect(sdk.instances).toHaveLength(1); + }); + it('reconnects immediately after system resume', async () => { const { getAcpClient, reconnectAcpAfterSystemResume } = await import('../acpConnection'); await getAcpClient(); diff --git a/ui/desktop/src/acp/acpConnection.ts b/ui/desktop/src/acp/acpConnection.ts index 8a88daff4..6f394536d 100644 --- a/ui/desktop/src/acp/acpConnection.ts +++ b/ui/desktop/src/acp/acpConnection.ts @@ -96,6 +96,9 @@ function recoverConnection(immediate: boolean): void { } }, () => { + if (pendingConnection === recoveryAttempt) { + pendingConnection = null; + } if (generation === connectionGeneration) { setRecovering(false); }