fix(clawrouter): cancel non-OK usage response body before throwing (#109525)

* fix(clawrouter): cancel non-OK usage response body before throwing

When fetchClawRouterUsage receives a non-OK response, it throws
without cancelling the response body.  The finally block calls
release() which cleans up the dispatcher, but the unconsumed
body stays open.

Cancel the body before throwing, matching the pattern in
guard-adapters (#109196) and fetchOpenRouterModels.

* test: use explicit ClawRouter placeholder token

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Monkey-wusky
2026-07-16 23:16:25 -07:00
committed by GitHub
co-authored by Peter Steinberger
parent f0aa2c892f
commit ea6ed38e20
2 changed files with 38 additions and 2 deletions
+37 -2
View File
@@ -205,14 +205,49 @@ describe("ClawRouter usage", () => {
expect(snapshot.billing).toEqual([{ type: "spend", amount: 0, unit: "USD" }]);
});
it("does not expose an upstream error body", async () => {
it("cancels non-OK usage response body before throwing", async () => {
let cancelled = false;
const response = new Response(
new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode("unauthorized"));
},
cancel() {
cancelled = true;
},
}),
{ status: 403 },
);
await expect(
fetchClawRouterUsage({
token: "proxy-key",
timeoutMs: 5000,
fetchGuard: mockFetchGuard(new Response("secret details", { status: 403 })),
fetchGuard: mockFetchGuard(response),
}),
).rejects.toThrow("ClawRouter usage request failed (HTTP 403)");
expect(cancelled).toBe(true);
});
it("observes peer closure when a real loopback server returns non-OK", async () => {
let responseClosed = false;
const { baseUrl, requests } = await startUsageServer((_req, res) => {
res.on("close", () => {
responseClosed = true;
});
res.writeHead(503, { "content-type": "text/plain" });
res.write("service unavailable");
});
await expect(
fetchClawRouterUsage({
token: "test-auth-token",
baseUrl,
timeoutMs: 5000,
}),
).rejects.toThrow("ClawRouter usage request failed (HTTP 503)");
expect(requests).toEqual(["GET /v1/usage"]);
expect(responseClosed).toBe(true);
});
it("bounds successful usage response bodies", async () => {
+1
View File
@@ -113,6 +113,7 @@ export async function fetchClawRouterUsage(params: {
);
try {
if (!response.ok) {
await response.body?.cancel().catch(() => undefined);
throw new Error(`ClawRouter usage request failed (HTTP ${response.status})`);
}
const payload = await readClawRouterUsagePayload(response, params.timeoutMs);