diff --git a/src/native/NativeMacOSProvider.ts b/src/native/NativeMacOSProvider.ts index c73cd7605..0d25f969c 100644 --- a/src/native/NativeMacOSProvider.ts +++ b/src/native/NativeMacOSProvider.ts @@ -283,6 +283,16 @@ class NativeMacOSClient implements AnalysisClient { async #listArchitectures( signal?: AbortSignal, ): Promise> { + // lipo reads only Mach-O; its refusal of a PE, ELF, or plist target is not + // a tool failure that a retry or `rea doctor` could repair. + if (this.target.format !== "mach-o") + return err( + new AnalysisCapabilityUnavailableError( + IDENTITY.id, + "list_architectures", + "Active artifact is not Mach-O.", + ), + ); const capture = await this.#run( "list_architectures", "lipo", diff --git a/tests/boundary/providers/native/nativeProvider.test.ts b/tests/boundary/providers/native/nativeProvider.test.ts index 7653d871c..d5be920ba 100644 --- a/tests/boundary/providers/native/nativeProvider.test.ts +++ b/tests/boundary/providers/native/nativeProvider.test.ts @@ -694,6 +694,31 @@ describe("native macOS provider failures and parsing", () => { } }); + it.each([ + { + ...machoTarget("/fixture.dll"), + format: "pe" as const, + executableRole: "shared-library" as const, + managed: false, + }, + { ...machoTarget("/fixture.elf"), format: "elf" as const }, + ])( + "refuses to list architectures of a $format target without running lipo", + async (target) => { + const runner = new CountingRunner(); + const result = await new NativeMacOSProvider({}, runner, "darwin") + .createClient(target) + .execute("list_architectures", {}); + if (result.ok) throw new Error("Expected a non-Mach-O refusal"); + expect(result.error._tag).toBe("AnalysisCapabilityUnavailableError"); + expect(projectAnalysisError(result.error)).toMatchObject({ + code: "capability_unavailable", + details: { reason: "Active artifact is not Mach-O." }, + }); + expect(runner.calls).toBe(0); + }, + ); + it("classifies pre-aborted requests before operation or runner discovery", async () => { const controller = new AbortController(); controller.abort();