diff --git a/src/cutter/CutterBridgeClient.ts b/src/cutter/CutterBridgeClient.ts index bc2d0d4d9f..d2e0dc5451 100644 --- a/src/cutter/CutterBridgeClient.ts +++ b/src/cutter/CutterBridgeClient.ts @@ -359,8 +359,9 @@ export class CutterBridgeClient { async listSessions( options: CutterDiscoveryOptions = {}, ): Promise { - this.#requestLimit(); const { signal } = options; + assertBridgeActive(signal, "list_cutter_sessions"); + this.#requestLimit(); const mismatches: number[] = []; const loaded = await this.#readDescriptors("list_cutter_sessions", signal); if (loaded.error !== undefined) @@ -463,9 +464,9 @@ export class CutterBridgeClient { readonly json: boolean; readonly signal?: AbortSignal; }): Promise { - this.#requestLimit(); try { assertBridgeActive(input.signal, "cutter_command"); + this.#requestLimit(); const descriptor = await this.#find(input.sessionId, input.signal); assertBridgeActive(input.signal, "cutter_command"); const advertised = diff --git a/tests/boundary/providers/cutterBridgeRequestLimit.test.ts b/tests/boundary/providers/cutterBridgeRequestLimit.test.ts index b7e7165311..0de0ef406d 100644 --- a/tests/boundary/providers/cutterBridgeRequestLimit.test.ts +++ b/tests/boundary/providers/cutterBridgeRequestLimit.test.ts @@ -10,7 +10,10 @@ import { CutterBridgeClient } from "../../../src/cutter/CutterBridgeClient.js"; import { runWithAnalysisOperationSnapshot } from "../../../src/domain/analysisOperationSnapshot.js"; import { CutterBridgeService } from "../../../src/cutter/CutterBridgeService.js"; import { ConfigurationError } from "../../../src/domain/configurationErrors.js"; -import { AnalysisResourceConstraintError } from "../../../src/domain/analysisErrorCore.js"; +import { + AnalysisCancelledError, + AnalysisResourceConstraintError, +} from "../../../src/domain/analysisErrorCore.js"; import { authenticatedServer, writeDescriptor, @@ -53,6 +56,32 @@ const clientFor = (directory: string, ceiling: string): CutterBridgeClient => { return client; }; +it("preserves pre-aborted cancellation before admitting a malformed bridge limit", async () => { + const client = new CutterBridgeClient({ + REA_BRIDGE_MAX_REQUEST_BYTES: "invalid", + }); + const reason = new Error("stop before discovery"); + const signal = AbortSignal.abort(reason); + for (const operation of [ + () => client.listSessions({ signal }), + () => + client.execute({ + sessionId: SESSION, + expectedGeneration: 0, + command: "ij", + json: false, + signal, + }), + ]) { + await expect(operation()).rejects.toSatisfy((error: unknown) => { + expect(error).toBeInstanceOf(AnalysisCancelledError); + expect(error).toHaveProperty("cause", reason); + return true; + }); + } + await client.close(); +}); + it("uses a runtime bridge ceiling for a Cutter client built from the launch value", async () => { const directory = await privateDirectory(); const server = authenticatedServer(TOKEN, SESSION, () => undefined);