From ea2f94d4de2241306158fecdd75f96cd34566694 Mon Sep 17 00:00:00 2001 From: Hill Patel Date: Tue, 13 Oct 2026 02:26:02 +0530 Subject: [PATCH] fix(wasm): stop appending WABT remediation to unrelated runtime-root failures (#2084) inspect_wasm_artifact's configuration-phase error handler assumed every non-OwnedCommandFailure thrown before the artifact phase was about WABT tool/directory resolution, and unconditionally appended "Supply WABT 1.0.42 via absolute REA_WABT_BIN_DIRECTORY; REA does not install tools." to the reason. WabtArtifactProvider.inspect() also allocates its PrivateRuntimeRoot during the configuration phase, so a PrivateRuntimeRootUnavailableError (the Windows native-authority capability, unrelated to WABT) got the same false remediation glued onto its own already-correct message, misdirecting anyone troubleshooting a correctly-configured WABT install toward re-checking WABT instead of the real blocker. Classify PrivateRuntimeRootUnavailableError before the generic configuration-phase branch, matching the existing convention already used in JavaScriptAnalysisWorker.ts and SourceMapCommand.ts: surface it as its own AnalysisCapabilityUnavailableError with no userMessage override, letting its own message stand. Verified against a real WABT 1.0.42 Windows release (hash-checked against GitHub release metadata) and a real compiled .wasm module: before the fix, `reason` read "REA Windows native controls are unavailable: ENOENT: ... Supply WABT 1.0.42 ..." even with a fully correct REA_WABT_BIN_DIRECTORY; after, `reason` reports only the real cause. --- src/wasm/WabtArtifactProvider.test.ts | 40 ++++++++++++++++++++++++++- src/wasm/WabtFailures.ts | 8 ++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/wasm/WabtArtifactProvider.test.ts b/src/wasm/WabtArtifactProvider.test.ts index 5a9e1cc45..74d481de3 100644 --- a/src/wasm/WabtArtifactProvider.test.ts +++ b/src/wasm/WabtArtifactProvider.test.ts @@ -3,7 +3,10 @@ import { join } from "node:path"; import { expect, it } from "vitest"; import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js"; import { WabtArtifactProvider } from "./WabtArtifactProvider.js"; -import { PrivateRuntimeRoot } from "../process/PrivateRuntimeRoot.js"; +import { + PrivateRuntimeRoot, + PrivateRuntimeRootUnavailableError, +} from "../process/PrivateRuntimeRoot.js"; import { spawnOwnedProviderProcess } from "../process/ProviderProcess.js"; import { OwnedCommandFailure } from "../process/OwnedCommand.js"; @@ -36,6 +39,41 @@ it("returns actionable unavailable without creating a workspace when tools are a }, }); }); + +it("reports an unavailable private runtime root without a misleading WABT remediation", async () => { + const toolRoot = await createTestTempDirectory("rea-wabt-tool-root-"); + for (const tool of ["wasm-validate", "wasm-objdump", "wasm2wat"]) + await writeFile( + join(toolRoot, tool + (process.platform === "win32" ? ".exe" : "")), + "source-owned executable seam", + { mode: 0o700 }, + ); + const provider = new WabtArtifactProvider( + { REA_WABT_BIN_DIRECTORY: toolRoot }, + undefined, + () => { + throw new PrivateRuntimeRootUnavailableError( + "REA Windows native controls are unavailable: probe failure", + ); + }, + ); + expect( + await provider.inspect({ + path: "/module.wasm", + glue_paths: [], + candidate_paths: [], + }), + ).toMatchObject({ + ok: false, + error: { + _tag: "AnalysisCapabilityUnavailableError", + userMessage: undefined, + message: expect.stringContaining( + "REA Windows native controls are unavailable", + ), + }, + }); +}); it.each([ "success", "invalid", diff --git a/src/wasm/WabtFailures.ts b/src/wasm/WabtFailures.ts index 520fa9ed3..ef5692f1b 100644 --- a/src/wasm/WabtFailures.ts +++ b/src/wasm/WabtFailures.ts @@ -11,6 +11,7 @@ import { } from "../domain/analysisErrorCore.js"; import { ProviderCleanupError } from "../domain/providerCleanupError.js"; import { OwnedCommandFailure } from "../process/OwnedCommand.js"; +import { PrivateRuntimeRootUnavailableError } from "../process/PrivateRuntimeRoot.js"; import { WABT_LIMITS, WABT_PROVIDER_IDENTITY } from "./WabtRelease.js"; /** Preserve lifecycle failures and diagnostic bytes instead of returning partial success. */ export const wabtFailure = ( @@ -70,6 +71,13 @@ export const wabtFailure = ( ); return new AnalysisOutputError("inspect_wasm_artifact", reason, options); } + if (cause instanceof PrivateRuntimeRootUnavailableError) + return new AnalysisCapabilityUnavailableError( + WABT_PROVIDER_IDENTITY.id, + "inspect_wasm_artifact", + reason, + { cause }, + ); if (phase === "configuration") return new AnalysisCapabilityUnavailableError( WABT_PROVIDER_IDENTITY.id,