diff --git a/src/pcap/PcapCommand.ts b/src/pcap/PcapCommand.ts index c7e3206ac0..166043a16b 100644 --- a/src/pcap/PcapCommand.ts +++ b/src/pcap/PcapCommand.ts @@ -181,14 +181,16 @@ export const runPcapCommand = async (options: { }, } : {}), - ...(cause instanceof OwnedCommandFailure && cause.cleanupFailure !== null - ? { - cleanup: { - reason: cause.cleanupFailure, - resources: cause.resources, - }, - } - : {}), + ...(cleanup !== undefined + ? { cleanup } + : cause instanceof OwnedCommandFailure && cause.cleanupFailure !== null + ? { + cleanup: { + reason: cause.cleanupFailure, + resources: cause.resources, + }, + } + : {}), }; if (cause instanceof OwnedCommandFailure && cause.reason === "cancelled") throw new AnalysisCancelledError(operation, { diff --git a/src/pcap/PcapProvider.ts b/src/pcap/PcapProvider.ts index 690f3246b2..04d9505440 100644 --- a/src/pcap/PcapProvider.ts +++ b/src/pcap/PcapProvider.ts @@ -347,6 +347,7 @@ export class PcapProvider { assertValidPcapCommandText(operation, stage, output); } catch (cause) { if ( + !commandReturned && retainedCommand !== undefined && cause instanceof AnalysisError && cause.cleanupIncomplete diff --git a/tests/boundary/pcap/pcapProviderCancellation.test.ts b/tests/boundary/pcap/pcapProviderCancellation.test.ts index a3a2818055..1e66cd42b7 100644 --- a/tests/boundary/pcap/pcapProviderCancellation.test.ts +++ b/tests/boundary/pcap/pcapProviderCancellation.test.ts @@ -144,9 +144,11 @@ const probeCleanupFixture = async (rereadCloseFailure = false) => { if (tsharkReads !== 3) return snapshot; if (!rereadCloseFailure) return { ...snapshot, sha256: "0".repeat(64) }; const resource = `executable-read:${tshark}`; + const lease = new AnalysisCleanupLease(async () => {}, [resource]); + args[4]?.retain({ kind: "lease", resource, lease }); throw new ArtifactReaderFailure("io", "injected executable read failure", { cleanup: { reason: "injected file close failure", resources: [resource] }, - cleanupLeases: [new AnalysisCleanupLease(async () => {}, [resource])], + cleanupLeases: [lease], }); }; const provider = new PcapProvider({ @@ -196,6 +198,8 @@ it("keeps an executable re-read's own cleanup live beside the probe's pending cl expect.stringMatching(/^process-group:/u), ]), ); + await provider.close(); + expect(result.error.cleanupIncomplete).toBe(false); }); it("availability surfaces a probe's pending cleanup instead of reporting the tool unavailable", async () => { diff --git a/tests/boundary/pcap/pcapProviderCrossPlatform.test.ts b/tests/boundary/pcap/pcapProviderCrossPlatform.test.ts index 28d87b1850..697d2e0e5a 100644 --- a/tests/boundary/pcap/pcapProviderCrossPlatform.test.ts +++ b/tests/boundary/pcap/pcapProviderCrossPlatform.test.ts @@ -230,6 +230,12 @@ it.each([0, 7])( expect(result.error.diagnostics).toMatchObject({ reason: "injected tool-probe cleanup failure", }); + else { + expect(result.error).toMatchObject({ + _tag: "AnalysisCapabilityUnavailableError", + capturedOutput: { exit_code: 7 }, + }); + } expect(projectAnalysisError(result.error).code).toBe("cleanup_incomplete"); const retry = await provider.execute({ operation: "inspect_pcap_client",