[GH-ISSUE #8366] [Bug] Byte-search cursor mismatch hides project profile and suggests schema repair #6565

Open
opened 2026-11-01 21:12:17 +00:00 by bsakaguchi · 1 comment
Owner

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

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

A valid Ghidra search_bytes continuation from a selected saved project is correctly rejected after opening the same target and query with a fresh-import profile. The user-facing message says only that analysis input is invalid; the structured issue says the cursor does not match the “target and query,” omitting the changed profile; and recovery directs the caller to inspect the input schema. The cursor is opaque and the request conforms to that schema.

Reproduction

  1. Open an owned Mach-O with a matching caller-selected Ghidra project using REA_GHIDRA_PROJECT_PATH and REA_GHIDRA_PROGRAM_NAME. Call search_bytes with {"pattern":"00","start":"0x100000000","end":"0x1000002df","limit":1} and retain next_cursor.
  2. A new MCP server selecting the same project and target accepts that cursor and returns subsequent matches, including when the page limit changes to 2.
  3. Start a fresh MCP server without the project settings, open the same target with Ghidra, and repeat the same query with the retained cursor. REA rejects it with invalid_request. The selected-project and fresh-import Evidence independently report different profile digests.
  4. Public rea search-bytes with the same project configuration, query and cursor succeeds; the fresh-import CLI invocation rejects it with the same error and recovery. A small read_bytes request after the MCP rejection succeeds.

The same error also occurred when changing the pattern with the selected project, and when reusing a fresh-import cursor on a different target with the same fresh profile. These controls show that the identity check is working; they do not establish that REA can determine which input differs from the opaque cursor.

Expected behavior

Keep rejecting incompatible continuations. Make recovery describe the actual binding possibilities—target, analysis profile/project, pattern, mask, bounds and document—and offer an applicable next step such as selecting the original context or starting a new search without the cursor. Attribute the issue to the cursor where appropriate. If the one-way cursor cannot establish the specific differing component, keep that cause unknown; do not claim a changed project or target from a hash mismatch alone.

Impact and scope

An agent can spend a recovery step inspecting an already-valid schema while missing that the caller-selected project/profile is part of the cursor identity. The target and saved project remained unchanged; no target was executed. Tested one saved project, two synthetic arm64 Mach-O targets and one pattern through MCP; same-project and changed-profile CLI paths were also exercised. Other cursor corruption cases, providers and platforms are unverified.

REA version

d54a4661da

Runtime and platform

Node 24.22.1; macOS 26.3 arm64; Ghidra 12.1.4 / JDK 21; TypeScript MCP SDK 2.3.1, full output-schema profile.

Target and artifact context

Owned target SHA-256 a6bd938bf4ae8031a33d270ca7698ee1ced5b78347a9c4fa3750b0e57f6b126c; selected-project profile f3b7b02dc225ed9f05c91226e26bc8c2cb371b86b4563b84fcd2f17c9233cde6; fresh-import profile 589427371e23b508f480b76cb045b44688b3890c0348f2d6b2505369ddb48ddb. The second target SHA-256 is 266a48ebeff34f2d28a2432dc0cf379872ad73f5212c63aac60ad6fe49033eb5. Both target hashes and selected-project regular-file hashes stayed unchanged.

Interface

MCP and CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

All three mismatches produced:

{
  "code": "invalid_request",
  "message": "Analysis input is invalid. Check the arguments and try again.",
  "recovery": {"kind":"inspect_input_contract","operation":"search_bytes"},
  "details": {"issues":[{"path":[],"reason":"invalid_value","message":"Byte search cursor does not match the target and query"}]}
}

Remediation says to read the tool schema or CLI usage and correct the fields. CLI --format json reported the same projected message and exited 1; same-project CLI continuation exited 0.

Analysis or suspected cause

Source-derived: the Ghidra bridge binds the cursor to target, profile, normalized pattern/mask, bounds and document, but emits a generic invalid-request message on mismatch. Generic AnalysisInputError projection then supplies schema-inspection recovery. Distinguishing a syntactic input error from a valid cursor for a different context is a design decision; this report does not require disclosing information the cursor does not retain.

Additional context

Related #8365 concerns live metadata freshness between otherwise accepted pages; #8349 reports a similar but separate decompile-resume profile recovery issue; #7661 concerns generic root-path attribution.

cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0

Same project path after an external project edit

On main 9ebbe67e, Ghidra 12.1.4 and an owned existing-project copy, I saved a search_bytes cursor for {pattern: "00", limit: 1}, closed the binary, used headless Ghidra to rename a function in that disposable project, and reopened the same target and project path. In compact and full stdio MCP profiles, reusing the cursor returned invalid_request with the generic schema-inspection recovery and Byte search cursor does not match the target and query; a fresh search without the cursor succeeded. In the full profile, omitting the required pattern returned the same schema-inspection recovery with a specific missing-field issue, a genuinely malformed-input control. Target SHA-256 510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a and the project files after the intentional edit were unchanged by REA. This tests one project edit, not every source of mismatch. The bridge's opaque binding does not identify which field changed.

Existing fix on changed project content

On unmodified PR #8420 head 728600aabb063c9e62fddaf363771ce75f07991d, real Ghidra 12.1.4 compact and full stdio MCP repeated the same-path saved-project edit control. The old cursor was rejected with the cursor issue path and differing_component: unknown; its remediation offered selecting the original context or starting a new search without the cursor, and did not suggest schema inspection. The new search succeeded. A missing-pattern control still received inspect_input_contract recovery. The target SHA-256 510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a and project files after the intentional edit remained unchanged by REA. Because the project contents changed at the same path in this control, selecting the same path alone would not restore the old profile; starting a fresh search did work. This is one controlled edit and does not establish all cursor recovery cases.

Existing fix, CLI control

On unmodified #8420 head 728600aabb063c9e62fddaf363771ce75f07991d, real rea search-bytes --format json with the cursor from the compact same-path project-edit control returned invalid_request with the cursor issue path, unknown differing component and a fresh-search option, without schema-inspection recovery. Running the same CLI search without the cursor succeeded. REA left the already edited disposable project and target SHA-256 510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a unchanged. This is one real CLI/provider control, not a complete cursor compatibility audit.

Originally created by @morluto on GitHub (Oct 31, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8366 ### 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 A valid Ghidra `search_bytes` continuation from a selected saved project is correctly rejected after opening the **same target and query** with a fresh-import profile. The user-facing message says only that analysis input is invalid; the structured issue says the cursor does not match the “target and query,” omitting the changed profile; and recovery directs the caller to inspect the input schema. The cursor is opaque and the request conforms to that schema. ### Reproduction 1. Open an owned Mach-O with a matching caller-selected Ghidra project using `REA_GHIDRA_PROJECT_PATH` and `REA_GHIDRA_PROGRAM_NAME`. Call `search_bytes` with `{"pattern":"00","start":"0x100000000","end":"0x1000002df","limit":1}` and retain `next_cursor`. 2. A new MCP server selecting the same project and target accepts that cursor and returns subsequent matches, including when the page limit changes to 2. 3. Start a fresh MCP server without the project settings, open the same target with Ghidra, and repeat the same query with the retained cursor. REA rejects it with `invalid_request`. The selected-project and fresh-import Evidence independently report different profile digests. 4. Public `rea search-bytes` with the same project configuration, query and cursor succeeds; the fresh-import CLI invocation rejects it with the same error and recovery. A small `read_bytes` request after the MCP rejection succeeds. The same error also occurred when changing the pattern with the selected project, and when reusing a fresh-import cursor on a different target with the same fresh profile. These controls show that the identity check is working; they do not establish that REA can determine which input differs from the opaque cursor. ### Expected behavior Keep rejecting incompatible continuations. Make recovery describe the actual binding possibilities—target, analysis profile/project, pattern, mask, bounds and document—and offer an applicable next step such as selecting the original context or starting a new search without the cursor. Attribute the issue to the cursor where appropriate. If the one-way cursor cannot establish the specific differing component, keep that cause unknown; do not claim a changed project or target from a hash mismatch alone. ### Impact and scope An agent can spend a recovery step inspecting an already-valid schema while missing that the caller-selected project/profile is part of the cursor identity. The target and saved project remained unchanged; no target was executed. Tested one saved project, two synthetic arm64 Mach-O targets and one pattern through MCP; same-project and changed-profile CLI paths were also exercised. Other cursor corruption cases, providers and platforms are unverified. ### REA version d54a4661da20382ad8456564a08569fdd24131c5 ### Runtime and platform Node 24.22.1; macOS 26.3 arm64; Ghidra 12.1.4 / JDK 21; TypeScript MCP SDK 2.3.1, full output-schema profile. ### Target and artifact context Owned target SHA-256 `a6bd938bf4ae8031a33d270ca7698ee1ced5b78347a9c4fa3750b0e57f6b126c`; selected-project profile `f3b7b02dc225ed9f05c91226e26bc8c2cb371b86b4563b84fcd2f17c9233cde6`; fresh-import profile `589427371e23b508f480b76cb045b44688b3890c0348f2d6b2505369ddb48ddb`. The second target SHA-256 is `266a48ebeff34f2d28a2432dc0cf379872ad73f5212c63aac60ad6fe49033eb5`. Both target hashes and selected-project regular-file hashes stayed unchanged. ### Interface MCP and CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence All three mismatches produced: ```json { "code": "invalid_request", "message": "Analysis input is invalid. Check the arguments and try again.", "recovery": {"kind":"inspect_input_contract","operation":"search_bytes"}, "details": {"issues":[{"path":[],"reason":"invalid_value","message":"Byte search cursor does not match the target and query"}]} } ``` Remediation says to read the tool schema or CLI usage and correct the fields. CLI `--format json` reported the same projected message and exited 1; same-project CLI continuation exited 0. ### Analysis or suspected cause Source-derived: the [Ghidra bridge](https://github.com/morluto/rea/blob/d54a4661da20382ad8456564a08569fdd24131c5/bridge/ghidra/ReaGhidraBridge.java#L812-L828) binds the cursor to target, profile, normalized pattern/mask, bounds and document, but emits a generic invalid-request message on mismatch. Generic `AnalysisInputError` projection then supplies schema-inspection recovery. Distinguishing a syntactic input error from a valid cursor for a different context is a design decision; this report does not require disclosing information the cursor does not retain. ### Additional context Related #8365 concerns live metadata freshness between otherwise accepted pages; #8349 reports a similar but separate decompile-resume profile recovery issue; #7661 concerns generic root-path attribution. cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 ### Same project path after an external project edit On main `9ebbe67e`, Ghidra 12.1.4 and an owned existing-project copy, I saved a `search_bytes` cursor for `{pattern: "00", limit: 1}`, closed the binary, used headless Ghidra to rename a function in that disposable project, and reopened the same target and project path. In compact and full stdio MCP profiles, reusing the cursor returned `invalid_request` with the generic schema-inspection recovery and `Byte search cursor does not match the target and query`; a fresh search without the cursor succeeded. In the full profile, omitting the required `pattern` returned the same schema-inspection recovery with a specific missing-field issue, a genuinely malformed-input control. Target SHA-256 `510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a` and the project files after the intentional edit were unchanged by REA. This tests one project edit, not every source of mismatch. The bridge's opaque binding does not identify which field changed. ### Existing fix on changed project content On unmodified PR #8420 head `728600aabb063c9e62fddaf363771ce75f07991d`, real Ghidra 12.1.4 compact and full stdio MCP repeated the same-path saved-project edit control. The old cursor was rejected with the `cursor` issue path and `differing_component: unknown`; its remediation offered selecting the original context or starting a new search without the cursor, and did not suggest schema inspection. The new search succeeded. A missing-`pattern` control still received `inspect_input_contract` recovery. The target SHA-256 `510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a` and project files after the intentional edit remained unchanged by REA. Because the project contents changed at the same path in this control, selecting the same path alone would not restore the old profile; starting a fresh search did work. This is one controlled edit and does not establish all cursor recovery cases. ### Existing fix, CLI control On unmodified #8420 head `728600aabb063c9e62fddaf363771ce75f07991d`, real `rea search-bytes --format json` with the cursor from the compact same-path project-edit control returned `invalid_request` with the `cursor` issue path, unknown differing component and a fresh-search option, without schema-inspection recovery. Running the same CLI search without the cursor succeeded. REA left the already edited disposable project and target SHA-256 `510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a` unchanged. This is one real CLI/provider control, not a complete cursor compatibility audit.
Author
Owner

@blake-andrews1 commented on GitHub (Nov 1, 2026):

Hey @morluto, thanks for all the details. I can pick this up if no one's on it. I can keep the change to the error message: surface the project-profile mismatch instead of the generic schema-repair hint, with a test. And can leave the design side to #8365.

<!-- gh-comment-id:6551567819 --> @blake-andrews1 commented on GitHub (Nov 1, 2026): Hey @morluto, thanks for all the details. I can pick this up if no one's on it. I can keep the change to the error message: surface the project-profile mismatch instead of the generic schema-repair hint, with a test. And can leave the design side to #8365.
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#6565
No description provided.