[GH-ISSUE #8217] [Bug] Changed Ghidra project loses its actionable error during provider launch #6494

Closed
opened 2026-11-01 21:11:38 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @morluto on GitHub (Oct 30, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8217

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

MCP or agent integration

Observed behavior

When the selected existing Ghidra project changes after open_binary commits its profile but before the first provider query copies it, REA correctly refuses the stale project. The top-level result is generic execution_failure with advice to run rea doctor; the project-change reason is only nested inside launcher diagnostics. Explicitly reopening the same target resolves a new profile and succeeds.

Reproduction

On macOS arm64, source 38433cfb4577b133d7598f9439c6448299cb44eb, Ghidra 12.1.4 and JDK 21:

  1. In a disposable project containing an analyzed self-built Mach-O, configure REA_GHIDRA_PROJECT_PATH and the exact root REA_GHIDRA_PROGRAM_NAME. Start an MCP server and open_binary the matching original binary.
  2. Before the first provider query, use a separate Ghidra headless -process ... -noanalysis script to rename a function in the caller-owned test project and save it. The target executable is not changed or run.
  3. procedure_pseudo_code on the known entry fails with execution_failure and generic doctor advice. Nested diagnostics say Selected artifact changed during acquisition and identify the selected project directory as a RegularFileChangedError.
  4. Call open_binary again with the same selected target and provider. binary_session now reports a new project fingerprint and analysis-profile digest; querying the entry succeeds and returns the newly annotated name.

As a separate control, after the provider was running on its private copy, I renamed the source-project function again with headless Ghidra. The active REA query still returned its original copied state; an explicit reopen committed another profile and returned the new annotation. Close succeeded and the target SHA-256 remained unchanged. One run exercised both boundaries.

Expected behavior

Return an actionable typed changed-project result when that physical mismatch is known, preserving the project selection and original cause. Tell the caller to wait for a stable project and reopen the selected target to resolve a fresh profile; do not suggest doctor as the primary fix or silently accept the changed project. The project may have changed between commitment and copy rather than during the copy itself, so avoid overclaiming the exact moment.

Impact and scope

The integrity boundary worked; the defect is error classification and recovery. An agent sees no top-level reason to rebind a deliberately updated project. This is one real macOS/headless MCP fixture; Windows, concurrent project writes during copying, other providers and cleanup failures are untested.

REA version

38433cfb4577b133d7598f9439c6448299cb44eb (PR #8187 branch)

Runtime and platform

Node 24.22.1; macOS 26.3 arm64; Ghidra 12.1.4; JDK 21.

Target and artifact context

Caller-owned disposable expanded .gpr project with an arm64 Mach-O; REA uses an owned private copy; separate headless scripts intentionally edited the disposable source project only.

Interface

MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed top-level: code: execution_failure, remediation Run rea doctor. Nested failure_cause.cause reports Selected artifact changed during acquisition with underlying RegularFileChangedError for the project directory. Reopening the same target changed the committed project fingerprint and then returned the edited name. A subsequent source-project edit did not silently change the active private copy; reopen loaded it.

Typed outcome or recovery expected

Consider preserving the existing artifact_changed error algebra and exact project path/cause through launch rollback, with open_binary recovery where applicable. Preserve any independent cleanup failure and do not report cleanup complete before it is established.

Analysis or suspected cause

Source inspection: GhidraProjectSnapshot classifies the copy fingerprint mismatch via ghidraFileFailure, which maps RegularFileChangedError to AnalysisArtifactChangedError. The observed nested launch error wraps that fact before public projection. The exact point to preserve the primary failure and all rollback/cleanup facts needs verification.

Additional context

Existing-project guide says project changes fail explicitly and reopen resolves the new profile. Related but distinct: #8169 concerns a project program whose recorded executable digest does not match the selected binary; #8216 concerns a missing selected program.

Originally created by @morluto on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8217 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area MCP or agent integration ### Observed behavior When the selected existing Ghidra project changes after `open_binary` commits its profile but before the first provider query copies it, REA correctly refuses the stale project. The top-level result is generic `execution_failure` with advice to run `rea doctor`; the project-change reason is only nested inside launcher diagnostics. Explicitly reopening the same target resolves a new profile and succeeds. ### Reproduction On macOS arm64, source `38433cfb4577b133d7598f9439c6448299cb44eb`, Ghidra 12.1.4 and JDK 21: 1. In a disposable project containing an analyzed self-built Mach-O, configure `REA_GHIDRA_PROJECT_PATH` and the exact root `REA_GHIDRA_PROGRAM_NAME`. Start an MCP server and `open_binary` the matching original binary. 2. Before the first provider query, use a separate Ghidra headless `-process ... -noanalysis` script to rename a function in the **caller-owned test project** and save it. The target executable is not changed or run. 3. `procedure_pseudo_code` on the known entry fails with `execution_failure` and generic doctor advice. Nested diagnostics say `Selected artifact changed during acquisition` and identify the selected project directory as a `RegularFileChangedError`. 4. Call `open_binary` again with the same selected target and provider. `binary_session` now reports a new project fingerprint and analysis-profile digest; querying the entry succeeds and returns the newly annotated name. As a separate control, after the provider was running on its private copy, I renamed the source-project function again with headless Ghidra. The active REA query still returned its original copied state; an explicit reopen committed another profile and returned the new annotation. Close succeeded and the target SHA-256 remained unchanged. One run exercised both boundaries. ### Expected behavior Return an actionable typed changed-project result when that physical mismatch is known, preserving the project selection and original cause. Tell the caller to wait for a stable project and reopen the selected target to resolve a fresh profile; do not suggest doctor as the primary fix or silently accept the changed project. The project may have changed between commitment and copy rather than during the copy itself, so avoid overclaiming the exact moment. ### Impact and scope The integrity boundary worked; the defect is error classification and recovery. An agent sees no top-level reason to rebind a deliberately updated project. This is one real macOS/headless MCP fixture; Windows, concurrent project writes during copying, other providers and cleanup failures are untested. ### REA version `38433cfb4577b133d7598f9439c6448299cb44eb` (PR #8187 branch) ### Runtime and platform Node 24.22.1; macOS 26.3 arm64; Ghidra 12.1.4; JDK 21. ### Target and artifact context Caller-owned disposable expanded `.gpr` project with an arm64 Mach-O; REA uses an owned private copy; separate headless scripts intentionally edited the disposable source project only. ### Interface MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed top-level: `code: execution_failure`, remediation `Run rea doctor`. Nested `failure_cause.cause` reports `Selected artifact changed during acquisition` with underlying `RegularFileChangedError` for the project directory. Reopening the same target changed the committed project fingerprint and then returned the edited name. A subsequent source-project edit did not silently change the active private copy; reopen loaded it. ### Typed outcome or recovery expected Consider preserving the existing `artifact_changed` error algebra and exact project path/cause through launch rollback, with `open_binary` recovery where applicable. Preserve any independent cleanup failure and do not report cleanup complete before it is established. ### Analysis or suspected cause Source inspection: [`GhidraProjectSnapshot`](https://github.com/morluto/rea/blob/38433cfb4577b133d7598f9439c6448299cb44eb/src/ghidra/GhidraProjectSnapshot.ts) classifies the copy fingerprint mismatch via `ghidraFileFailure`, which maps `RegularFileChangedError` to `AnalysisArtifactChangedError`. The observed nested launch error wraps that fact before public projection. The exact point to preserve the primary failure and all rollback/cleanup facts needs verification. ### Additional context [Existing-project guide](https://github.com/morluto/rea/blob/38433cfb4577b133d7598f9439c6448299cb44eb/docs/installation.md#reuse-an-existing-ghidra-project) says project changes fail explicitly and reopen resolves the new profile. Related but distinct: #8169 concerns a project program whose recorded executable digest does not match the selected binary; #8216 concerns a missing selected program.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.

<!-- gh-comment-id:6538799868 --> @morluto commented on GitHub (Oct 31, 2026): Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.
Author
Owner

@macjayz commented on GitHub (Oct 31, 2026):

I'd like to take this one. I fixed the sibling case, #8216, in #8222: a missing project program now returns a typed configuration_invalid result naming REA_GHIDRA_PROGRAM_NAME instead of generic doctor advice. This issue has the same shape (a known project condition hidden behind execution_failure), in the same projection code, so I'd apply the same approach here.

Plan:

  1. When the launch failure's cause is the existing artifact-change refusal for the selected project directory (Selected artifact changed during acquisition, RegularFileChangedError), project it as a typed changed-project result rather than generic execution_failure. Keep the original cause and diagnostics attached.
  2. Remediation: the selected project changed after open_binary committed its profile. Call open_binary again for the same target to resolve the current project. No rea doctor advice for this case.
  3. Keep the refusal fail-closed: no automatic reopen, no fallback to a fresh import, no change to the active session's private copy.
  4. Tests: project changed between open_binary and the first query, plus a control where an unrelated launch failure keeps the generic result.

Both changes touch the same projection in GhidraProviderClient.ts, so I'll build this on top of #8222 and rebase onto main once that lands.

<!-- gh-comment-id:6539989699 --> @macjayz commented on GitHub (Oct 31, 2026): I'd like to take this one. I fixed the sibling case, #8216, in #8222: a missing project program now returns a typed `configuration_invalid` result naming `REA_GHIDRA_PROGRAM_NAME` instead of generic doctor advice. This issue has the same shape (a known project condition hidden behind `execution_failure`), in the same projection code, so I'd apply the same approach here. Plan: 1. When the launch failure's cause is the existing artifact-change refusal for the selected project directory (`Selected artifact changed during acquisition`, `RegularFileChangedError`), project it as a typed changed-project result rather than generic `execution_failure`. Keep the original cause and diagnostics attached. 2. Remediation: the selected project changed after `open_binary` committed its profile. Call `open_binary` again for the same target to resolve the current project. No `rea doctor` advice for this case. 3. Keep the refusal fail-closed: no automatic reopen, no fallback to a fresh import, no change to the active session's private copy. 4. Tests: project changed between `open_binary` and the first query, plus a control where an unrelated launch failure keeps the generic result. Both changes touch the same projection in `GhidraProviderClient.ts`, so I'll build this on top of #8222 and rebase onto `main` once that lands.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Real-provider validation of exact PR #8273 head e9bc2e294dea638c0971803b303f99e4930cc935, on macOS arm64 with Ghidra 12.1.4, JDK 21 and real stdio MCP. I copied the original source-owned two-program project to a disposable location and edited only that copy using a separate headless Ghidra script; the executable was never run or changed.

  1. open_binary committed the selected project, then the headless script finished renaming its function before the first provider query. That query returned artifact_changed, included the selected project directory and recommended reopening with open_binary.
  2. Explicitly reopening returned the first edited name and comment. I then changed the source project again while REA's private copy was active: the active session still returned its original copied name. A second explicit reopen returned the new name.
  3. The source-project content hashes stayed unchanged during each REA query after the deliberate external edits; the target digest stayed unchanged and close_binary succeeded.

One wording limitation remains: this controlled edit had completed before the query began, yet the top-level message says the artifact changed "during acquisition" and the detailed reason says "while being read". The observed condition is that the project no longer matches the previously committed profile; the exact time of change within or before copying is not established by that mismatch alone. The existing typed recovery is useful, but the timing claim should not be interpreted more narrowly than the evidence. This is one macOS/project fixture; cleanup failure, concurrent writes during copying, CLI and Windows were not exercised.

<!-- gh-comment-id:6540839638 --> @morluto commented on GitHub (Oct 31, 2026): Real-provider validation of exact [PR #8273](https://github.com/morluto/rea/pull/8273) head `e9bc2e294dea638c0971803b303f99e4930cc935`, on macOS arm64 with Ghidra 12.1.4, JDK 21 and real stdio MCP. I copied the original source-owned two-program project to a disposable location and edited only that copy using a separate headless Ghidra script; the executable was never run or changed. 1. `open_binary` committed the selected project, then the headless script finished renaming its function **before** the first provider query. That query returned `artifact_changed`, included the selected project directory and recommended reopening with `open_binary`. 2. Explicitly reopening returned the first edited name and comment. I then changed the source project again while REA's private copy was active: the active session still returned its original copied name. A second explicit reopen returned the new name. 3. The source-project content hashes stayed unchanged during each REA query after the deliberate external edits; the target digest stayed unchanged and `close_binary` succeeded. One wording limitation remains: this controlled edit had completed before the query began, yet the top-level message says the artifact changed "during acquisition" and the detailed reason says "while being read". The observed condition is that the project no longer matches the previously committed profile; the exact time of change within or before copying is not established by that mismatch alone. The existing typed recovery is useful, but the timing claim should not be interpreted more narrowly than the evidence. This is one macOS/project fixture; cleanup failure, concurrent writes during copying, CLI and Windows were not exercised.
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#6494
No description provided.