diff --git a/src/agents/embedded-agent-runner/openrouter-model-capabilities.test.ts b/src/agents/embedded-agent-runner/openrouter-model-capabilities.test.ts index 37d385565a18..270d74e7060b 100644 --- a/src/agents/embedded-agent-runner/openrouter-model-capabilities.test.ts +++ b/src/agents/embedded-agent-runner/openrouter-model-capabilities.test.ts @@ -105,6 +105,21 @@ describe("openrouter-model-capabilities", () => { }); }); + it("cancels failed OpenRouter catalog response bodies", async () => { + await withOpenRouterStateDir(async () => { + const response = new Response("temporarily unavailable", { status: 503 }); + const cancel = vi.spyOn(response.body!, "cancel").mockResolvedValue(undefined); + const fetchSpy = vi.fn(async () => response); + vi.stubGlobal("fetch", fetchSpy); + + const module = await importOpenRouterModelCapabilities("failed-catalog-response"); + await module.loadOpenRouterModelCapabilities("acme/missing-model"); + + expect(fetchSpy).toHaveBeenCalledTimes(1); + expect(cancel).toHaveBeenCalledOnce(); + }); + }); + it("uses endpoint-specific OpenRouter context length when top_provider reports one", async () => { await withOpenRouterStateDir(async () => { vi.stubGlobal( diff --git a/src/agents/embedded-agent-runner/openrouter-model-capabilities.ts b/src/agents/embedded-agent-runner/openrouter-model-capabilities.ts index 8c6ea8ecb8c4..5cb8c0d93158 100644 --- a/src/agents/embedded-agent-runner/openrouter-model-capabilities.ts +++ b/src/agents/embedded-agent-runner/openrouter-model-capabilities.ts @@ -172,6 +172,12 @@ function parseModel(model: OpenRouterApiModel): OpenRouterModelCapabilities { }; } +async function cancelUnreadResponseBody(response: Response | undefined): Promise { + if (response && !response.bodyUsed) { + await response.body?.cancel().catch(() => undefined); + } +} + // --------------------------------------------------------------------------- // API fetch // --------------------------------------------------------------------------- @@ -179,10 +185,11 @@ function parseModel(model: OpenRouterApiModel): OpenRouterModelCapabilities { async function doFetch(): Promise { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); + let response: Response | undefined; try { const fetchFn = resolveProxyFetchFromEnv() ?? globalThis.fetch; - const response = await fetchFn(OPENROUTER_MODELS_URL, { + response = await fetchFn(OPENROUTER_MODELS_URL, { signal: controller.signal, }); @@ -210,6 +217,7 @@ async function doFetch(): Promise { log.warn(`Failed to fetch OpenRouter models: ${message}`); } finally { clearTimeout(timeout); + await cancelUnreadResponseBody(response); } }