From cacd98304e80a5ceaef9eee26c8033f355aa1e8e Mon Sep 17 00:00:00 2001 From: Wynne668 Date: Mon, 20 Jul 2026 11:12:27 +0800 Subject: [PATCH] fix(sandbox): cancel CDP probe response bodies (#109767) * fix(sandbox): cancel unsuccessful CDP probe responses Release unread response bodies when sandbox CDP startup probes receive non-success statuses so retry loops do not retain transport resources. Co-authored-by: Cursor * fix(sandbox): cancel all CDP probe responses * test(sandbox): simplify CDP probe cleanup coverage Signed-off-by: sallyom --------- Signed-off-by: sallyom Co-authored-by: Cursor Co-authored-by: sallyom --- src/agents/sandbox/browser.create.test.ts | 40 +++++++++++++++++++++++ src/agents/sandbox/browser.ts | 1 + 2 files changed, 41 insertions(+) diff --git a/src/agents/sandbox/browser.create.test.ts b/src/agents/sandbox/browser.create.test.ts index 9d798daface3..3a80db904250 100644 --- a/src/agents/sandbox/browser.create.test.ts +++ b/src/agents/sandbox/browser.create.test.ts @@ -774,6 +774,46 @@ describe("ensureSandboxBrowser create args", () => { ); }); + it.each([200, 503])( + "cancels the CDP probe response body after a %i startup probe", + async (status) => { + const cancels: Array> = []; + vi.spyOn(globalThis, "fetch").mockImplementation(async () => { + const cancel = vi.fn().mockResolvedValue(undefined); + cancels.push(cancel); + return { + ok: status === 200, + body: { cancel }, + } as never; + }); + bridgeMocks.startBrowserBridgeServer.mockImplementationOnce(async (params) => { + await params.onEnsureAttachTarget?.({}); + throw new Error("probe completed before bridge creation"); + }); + + const cfg = buildConfig(false); + cfg.browser.autoStartTimeoutMs = 50; + + await expect( + ensureTestSandboxBrowser({ + scopeKey: "session:test", + workspaceDir: "/tmp/workspace", + agentWorkspaceDir: "/tmp/workspace", + cfg, + }), + ).rejects.toThrow( + status === 200 + ? "probe completed before bridge creation" + : "hung container has been forcefully removed", + ); + + expect(cancels).not.toHaveLength(0); + for (const cancel of cancels) { + expect(cancel).toHaveBeenCalledOnce(); + } + }, + ); + it("keeps a stalled CDP request inside the browser startup deadline", async () => { const sockets = new Set(); let requestPath: string | undefined; diff --git a/src/agents/sandbox/browser.ts b/src/agents/sandbox/browser.ts index 4d57a22ba0df..4318b6004d47 100644 --- a/src/agents/sandbox/browser.ts +++ b/src/agents/sandbox/browser.ts @@ -99,6 +99,7 @@ async function waitForSandboxCdp(params: { headers: { Authorization: buildSandboxCdpAuthHeader(params.authToken) }, signal: ctrl.signal, }); + await res.body?.cancel().catch(() => undefined); if (res.ok) { return true; }