[PR #2011] [MERGED] fix(ghidra): retain launcher command outcome in launch failure diagnostics #4965

Closed
opened 2026-10-31 00:57:28 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2011
Author: @sx4im
Created: 10/12/2026
Status: ✅ Merged
Merged: 10/12/2026
Merged by: @morluto

Base: main ← Head: fix/ghidra-launch-failure-diagnostics


📝 Commits (2)

  • 9a0d672 fix(ghidra): retain launcher command outcome in launch failure diagnostics
  • deab01f test(ghidra): skip POSIX JVM fixture tests on Windows

📊 Changes

3 files changed (+247 additions, -3 deletions)

View changed files

➕ src/ghidra/GhidraLaunchFailure.test.ts (+166 -0)
📝 src/ghidra/GhidraLauncher.ts (+42 -3)
📝 src/ghidra/GhidraSessionError.ts (+39 -0)

📄 Description

Summary

Fixes #1992. A failing Ghidra headless launch returned only the GhidraLaunchError name and the generic "Ghidra headless launch failed" message. The launcher command exit code/signal and JVM stderr were dropped, so a fault in REA process spawning was indistinguishable from a broken Ghidra install. The launch error now retains the failed command outcome in the shared captured-output envelope, and the session failure_cause projects it as launch_outcome, following the compiler_outcome pattern already in the codebase.

Problem and expected behavior

Smallest trigger: the POSIX LaunchSupport probe (<javaHome>/bin/java ... LaunchSupport <installRoot> -envvars / -vmargs in ghidraJavaLaunch) exits non-zero, e.g. a broken JDK. Expected: failure diagnostics retain the command outcome detail (exit/signal plus bounded output), the way provider-selection failures and unexpected request failures already do. Actual: describeCause kept only the GhidraLaunchError name, message, and code, and the nested exec error stdout/stderr never surfaced.

Change and scope

  • src/ghidra/GhidraLauncher.ts: GhidraLaunchError gains an optional capturedOutput in the shared AnalysisCapturedOutput envelope. The launch catch block extracts it from the cause with the existing execFileOutputFailure reader, on both the primary failure and the cleanup-incomplete rollback path. Non-command failures (missing JDK home, aborts) are unchanged: no captured output.
  • src/ghidra/GhidraSessionError.ts: describeErrorCause now emits launch_outcome for GhidraLaunchError causes, with token-redacted stdout/stderr bounded to 8 KiB per stream via boundedUtf8Text, an explicit truncated flag with full byte counts when cut, plus exit_code/signal. This mirrors causeCompilerOutcome in the same file.
  • Not included: the primary failure message is unchanged, no spawn supervision changes, no doctor output changes.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Ghidra provider adapter (launch) and Ghidra session failure projection. No domain contract changes.
  • CLI and MCP/tool-catalog contract: none. The failure_cause diagnostics record gains an optional launch_outcome object; existing fields are unchanged.
  • Provider, bridge, target-format, or platform compatibility: none.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Process execution, authorization, cleanup, or containment impact: none; no new processes and no cleanup changes.
  • Generated metadata, package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated: src/ghidra/GhidraLaunchFailure.test.ts (4 tests).

  • Base reproduction: a stand-in bin/java script exiting 3 with known stderr makes GhidraHeadlessLauncher.launch() fail; on the base revision the error carried no capturedOutput (3 of the 4 tests failed), and all 4 pass with the fix.

  • User-visible CLI/MCP output: session failure diagnostics now include e.g. failure_cause: { name: "GhidraLaunchError", message: "Ghidra headless launch failed", launch_outcome: { exit_code: 3, signal: null, stdout: "", stderr: "jvm probe exploded", truncated: false } }.

  • Remaining proof gaps: no real Ghidra install on this Linux host, so the probe path was exercised with a failing stand-in binary rather than a real JDK.

  • Observed, derived, and inferred claims remain distinguishable.

  • Artifact identity, source provenance, and failed attempts remain preserved.

  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npx vitest run src/ghidra/GhidraLaunchFailure.test.ts — 4 passed (3 failed before the fix)
  • npx vitest run src/ghidra/ src/domain/analysisErrorProjection.test.ts — 407 passed
  • npm run check:fast — typecheck and lint (including module boundaries) successful
  • npx oxfmt --check on the changed files — clean
  • npm run verify:test-discovery — 844 test files discovered exactly once

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: no real Ghidra on this host; the launch path was verified with a failing stand-in JVM binary on Linux (platform parameter "darwin" to mirror the issue macOS report).
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: retained stderr is token-redacted and bounded to 8 KiB per stream at projection, with explicit truncation.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/2011 **Author:** [@sx4im](https://github.com/sx4im) **Created:** 10/12/2026 **Status:** ✅ Merged **Merged:** 10/12/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/ghidra-launch-failure-diagnostics` --- ### 📝 Commits (2) - [`9a0d672`](https://github.com/morluto/rea/commit/9a0d6723bfd52fa94dfb10aa24cf6e793904a887) fix(ghidra): retain launcher command outcome in launch failure diagnostics - [`deab01f`](https://github.com/morluto/rea/commit/deab01f58df3bb74529e02d2bc72eed41d74b539) test(ghidra): skip POSIX JVM fixture tests on Windows ### 📊 Changes **3 files changed** (+247 additions, -3 deletions) <details> <summary>View changed files</summary> ➕ `src/ghidra/GhidraLaunchFailure.test.ts` (+166 -0) 📝 `src/ghidra/GhidraLauncher.ts` (+42 -3) 📝 `src/ghidra/GhidraSessionError.ts` (+39 -0) </details> ### 📄 Description ## Summary Fixes #1992. A failing Ghidra headless launch returned only the `GhidraLaunchError` name and the generic "Ghidra headless launch failed" message. The launcher command exit code/signal and JVM stderr were dropped, so a fault in REA process spawning was indistinguishable from a broken Ghidra install. The launch error now retains the failed command outcome in the shared captured-output envelope, and the session `failure_cause` projects it as `launch_outcome`, following the `compiler_outcome` pattern already in the codebase. ## Problem and expected behavior Smallest trigger: the POSIX LaunchSupport probe (`<javaHome>/bin/java ... LaunchSupport <installRoot> -envvars` / `-vmargs` in `ghidraJavaLaunch`) exits non-zero, e.g. a broken JDK. Expected: failure diagnostics retain the command outcome detail (exit/signal plus bounded output), the way provider-selection failures and unexpected request failures already do. Actual: `describeCause` kept only the `GhidraLaunchError` name, message, and code, and the nested exec error stdout/stderr never surfaced. ## Change and scope - `src/ghidra/GhidraLauncher.ts`: `GhidraLaunchError` gains an optional `capturedOutput` in the shared `AnalysisCapturedOutput` envelope. The launch catch block extracts it from the cause with the existing `execFileOutputFailure` reader, on both the primary failure and the cleanup-incomplete rollback path. Non-command failures (missing JDK home, aborts) are unchanged: no captured output. - `src/ghidra/GhidraSessionError.ts`: `describeErrorCause` now emits `launch_outcome` for `GhidraLaunchError` causes, with token-redacted stdout/stderr bounded to 8 KiB per stream via `boundedUtf8Text`, an explicit `truncated` flag with full byte counts when cut, plus `exit_code`/`signal`. This mirrors `causeCompilerOutcome` in the same file. - Not included: the primary failure message is unchanged, no spawn supervision changes, no doctor output changes. ## Contract and boundary impact - Semantic owner and earliest changed stage: Ghidra provider adapter (launch) and Ghidra session failure projection. No domain contract changes. - CLI and MCP/tool-catalog contract: none. The `failure_cause` diagnostics record gains an optional `launch_outcome` object; existing fields are unchanged. - Provider, bridge, target-format, or platform compatibility: none. - Evidence, artifact, provenance, or reconstruction contract: none. - Process execution, authorization, cleanup, or containment impact: none; no new processes and no cleanup changes. - Generated metadata, package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: `src/ghidra/GhidraLaunchFailure.test.ts` (4 tests). - Base reproduction: a stand-in `bin/java` script exiting 3 with known stderr makes `GhidraHeadlessLauncher.launch()` fail; on the base revision the error carried no `capturedOutput` (3 of the 4 tests failed), and all 4 pass with the fix. - User-visible CLI/MCP output: session failure diagnostics now include e.g. `failure_cause: { name: "GhidraLaunchError", message: "Ghidra headless launch failed", launch_outcome: { exit_code: 3, signal: null, stdout: "", stderr: "jvm probe exploded", truncated: false } }`. - Remaining proof gaps: no real Ghidra install on this Linux host, so the probe path was exercised with a failing stand-in binary rather than a real JDK. - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npx vitest run src/ghidra/GhidraLaunchFailure.test.ts` — 4 passed (3 failed before the fix) - `npx vitest run src/ghidra/ src/domain/analysisErrorProjection.test.ts` — 407 passed - `npm run check:fast` — typecheck and lint (including module boundaries) successful - `npx oxfmt --check` on the changed files — clean - `npm run verify:test-discovery` — 844 test files discovered exactly once ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: no real Ghidra on this host; the launch path was verified with a failing stand-in JVM binary on Linux (platform parameter "darwin" to mirror the issue macOS report). - Package or release metadata impact: none. - Security, privacy, process, or containment review: retained stderr is token-redacted and bounded to 8 KiB per stream at projection, with explicit truncation. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:57:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4965
No description provided.