[GH-ISSUE #8743] [Bug] Kuna project failure points to removed per-function records #7531

Open
opened 2026-11-03 08:19:41 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @morluto on GitHub (Nov 3, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8743

Before submitting

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

Area

Native analysis or decompilation

Observed behavior

When Kuna's entire project decompile fails, REA reports Kuna's diagnostic telling the caller to see the per-function error records, but the REA-owned temporary project that contained those records has already been cleaned up when the error is returned. The error retains bounded process output, including some diagnostic text; it does not expose the per-function output records from the removed project.

Reproduction

On REA main 9ebbe67e05af08b567e215340da993c08dae378f, macOS arm64, Node 24.22.1 and released Kuna 1.989, use a fresh cache and an owned two-function Mach-O. A temporary test wrapper injects KUNA_JOBS_FAULT=panic:0x100000328,panic:0x100000340 only into decompile-project, with two jobs, --max-fn-seconds 1 and --jobs-chunk 1.

  1. Open the target with Kuna through stdio MCP and call procedure_assembly for 0x100000328. The project exits 1 because both functions failed. REA's error repeats Kuna's "see the per-function error records" instruction. Its captured stdout says where Kuna wrote the project.
  2. Before closing the session, check that reported project path: it no longer exists. Repeat in both compact and full MCP profiles.
  3. In the same session, procedure_pseudo_code for the function succeeds via a separate standalone decompile, which has different whole-program context.
  4. Run the same injected decompile-project directly with a caller-owned output directory. Kuna exits 1 but leaves its .c output, containing individual Function ... (error: worker chunk failed ...) records for both addresses.

These are deliberately injected failures, not naturally observed worker crashes. The source-owned target SHA-256 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930 was unchanged and the target was not executed.

Expected behavior

Do not tell an agent to inspect records that REA has removed. Consider preserving useful bounded per-function failure observations before cleanup, or explicitly say they are unavailable and point to a real recovery path. Preserve cleanup ownership and the provider failure rather than publishing a failed project as complete. Distinguish standalone pseudocode from project-dependent operations.

Impact and scope

The caller receives aggregate captured process diagnostics but cannot follow the provider's instruction to inspect its per-function records. Other failure modes, large error records and other platforms were not exercised; a CLI control is recorded below. The control does not establish frequency or imply that keeping all temporary files is safe or desirable.

REA version

9ebbe67e05af08b567e215340da993c08dae378f

Runtime and platform

macOS arm64; Node 24.22.1; Kuna 1.989 behind the described test-only wrapper.

Target and artifact context

Owned, unexecuted arm64 Mach-O with two discovered functions; read-only static analysis.

Interface

MCP

Earliest failure stage

Provider failure projection and cleanup

Relevant output or evidence

Kuna direct output contains Function: _audit_value @ 0x100000328 (error: worker chunk failed ...) and the corresponding _main record. REA returns the nonzero exit and output diagnostics but the temporary project named in the captured stdout no longer exists before session close.

Typed outcome or recovery expected

Explicitly distinguish complete project, failed project, unavailable failure records and successful standalone fallback. Bound retained data; preserve cancellation and cleanup-retry semantics.

Analysis or suspected cause

Source-derived: KunaProjectCache stages exports and retires owned, stopped staging on failure. This is appropriate cleanup ownership; the observed mismatch is passing along an instruction that presupposes a retained provider output. A fix must not assume every failing provider has created a valid project.

Additional context

Related #8167 concerns failures within a completed project; #8740 concerns contradictory standalone-fallback Evidence. cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0

CLI and producing-boundary control

With the same two-function target and deliberate panic injection, a fresh-cache rea function <target> 0x100000328 --provider kuna --format json on main 9ebbe67e exited 1 with capability_unavailable for analyze_function. The error repeated the instruction to see per-function records and included the temporary project path in captured stdout; that project path did not exist after the command. The target SHA-256 remained 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930.

Source inspection: exportKunaProject returns the command failure before it parses the export summary, checks output files, or calls its existing streaming function-block parser. KunaProjectCache then retires staging only after the build is proven stopped. A possible partial-diagnostic design would therefore need its own admission and resource bounds for nonzero-exit output, while preserving the original command failure and cleanup/cancellation results; the success-path parser and publication do not automatically establish validity of failed output.

Partial assembly in the failed direct export

The caller-owned direct Kuna 1.989 output from the injected all-worker failure above also contained arm64.asm. Its six instruction addresses and mnemonics for _audit_value matched a separate direct Kuna disassemble query and independent otool -tvV on this fixture. The static target digest remained 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930. In the REA control, procedure_assembly still returned project-dependent unavailability after the same injected failure because REA retired the failed staging directory. This demonstrates potentially useful partial output in one deliberately failed project, not that every failed export writes valid or complete assembly, or that a partial project should be published as successful. Direct Kuna disassembly is a separately invoked external workflow, not a demonstrated REA fallback.

Source inspection for a bounded partial-diagnostic design: the successful-export .c scanner verifies headers against a completed summary, caps each retained structural line at 64 KiB, and accumulates retained header lines before that validation; that per-line cap is not a total record or memory bound. The nonzero-exit path returns before completed-summary parsing. Reusing that scanner on failed output therefore would not by itself establish bounded partial-diagnostic admission or completeness. Keep the primary process failure, incomplete coverage, resource limits and staging ownership explicit, and do not infer that the assembly was validated merely from this control.

Existing REA provider alternative

In fresh compact and full stdio MCP sessions on main 9ebbe67e with the same deliberate all-worker failure and Kuna 1.989, procedure_assembly failed and standalone pseudocode succeeded. inspect_native_instruction and read_bytes returned capability_unavailable because Kuna declares neither operation, with generic recovery. After closing that binary and reopening the same fixture with Ghidra 12.1.4 in the same MCP server, inspect_native_instruction decoded the entry as sub and read_bytes returned the complete first four bytes ff4300d1 in both profiles. The target digest stayed 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930 and it was not executed.

This is one independently available provider switch, not a Kuna fallback, partial-project recovery, or a guarantee that Ghidra is installed or supports another target. Switching requires closing and reopening the binary; preserve any needed session Evidence first. The Kuna session's capability groups did not list the unsupported instruction and byte operations; absence is not proof that the agent discovered an alternative. The fault was injected, not a natural-failure-frequency observation.

Provider-switch Evidence handoff

In fresh-cache compact and full MCP sessions on main 9ebbe67e, after the deliberately failed Kuna project I exported the successful standalone pseudocode Evidence, closed the binary, reopened the same unexecuted fixture with Ghidra 12.1.4 in the same MCP server, and imported the bundle. get_evidence_bundle contained the original Kuna pseudocode record exactly unchanged while the Ghidra instruction and byte queries succeeded as above. The target digest stayed 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930. This tests one in-process provider switch and explicit bundle handoff, not automatic cross-provider fallback or a fresh-server handoff. inspect_analysis_view reported the pseudocode operation unsupported for analysis views; bundle retrieval is the tested path for that record.

Failure receipt and bundle scope

In another fresh-cache real Kuna 1.989 session with the same injected failure, get_evidence_bundle immediately after the failed procedure_assembly call returned no records in either MCP profile. The error response itself retained the aggregate failure and captured diagnostic details, but had no Evidence ID. After the separate successful pseudocode call, its Evidence could be exported and imported across the Ghidra provider switch as described above. The target digest was unchanged. Therefore this tested bundle handoff preserves the successful observation, not the original failed-call receipt or removed per-function records. This is an observed scope of one failure and does not establish that all REA errors should become Evidence records; any design for preserving failed attempts needs an explicit contract and bounds.

Failure context within successful fallback Evidence

In the provider-switch bundle control, the retained standalone pseudocode record did preserve raw_result.project_export.state: failed and the aggregate failure reason in both MCP profiles. That reason repeats Kuna's instruction to see the now-removed per-function records, so it is useful context but not the original error receipt or the individual records. On main 9ebbe67e its limitations also included the contradictory "still building" wording tracked by #8740. Repeating the same real-provider handoff on #8742 head d2859517d9071828eb65e42df9b25e3011f6aa2a removed that timing contradiction, kept the single-function and failed-project caveats, and retained the same type of aggregate failure context; it did not recover the removed records. Both controls used the deliberate worker faults and preserved the target digest 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930. This qualifies the preceding receipt observation: some failure context is portable when a successful fallback record exists.

Reopen recovery control

On REA 9ebbe67e with Kuna 1.989, a test-only wrapper injected the same all-worker panic until a marker was removed. In compact and full stdio MCP sessions, procedure_assembly failed as above; removing the fault and repeating the call in the same binary session still returned capability_unavailable. Closing and reopening the target in the same MCP server then produced assembly successfully. This validates the suggested reopen step for one resolved, injected failure; it does not recover the deleted per-function records or establish recovery from other failure and cleanup modes. The unexecuted target's SHA-256 remained 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930.

Explicit record-availability guidance

Draft #8789 reports that REA does not expose failed-project per-function records in the project-dependent error and standalone fallback Evidence, while preserving the upstream failure and captured output. A real Kuna 1.989 wrapper-injected all-worker failure reproduced the error and standalone fallback in compact/full MCP and the error in CLI; the target stayed unchanged and was not executed. The change does not recover individual records; the separate bounded partial-diagnostic question remains open. #8742 separately addresses the stale building-state limitation.

Originally created by @morluto on GitHub (Nov 3, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8743 ### 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 Native analysis or decompilation ### Observed behavior When Kuna's entire project decompile fails, REA reports Kuna's diagnostic telling the caller to see the per-function error records, but the REA-owned temporary project that contained those records has already been cleaned up when the error is returned. The error retains bounded process output, including some diagnostic text; it does not expose the per-function output records from the removed project. ### Reproduction On REA main `9ebbe67e05af08b567e215340da993c08dae378f`, macOS arm64, Node 24.22.1 and released Kuna 1.989, use a fresh cache and an owned two-function Mach-O. A temporary test wrapper injects `KUNA_JOBS_FAULT=panic:0x100000328,panic:0x100000340` only into `decompile-project`, with two jobs, `--max-fn-seconds 1` and `--jobs-chunk 1`. 1. Open the target with Kuna through stdio MCP and call `procedure_assembly` for `0x100000328`. The project exits 1 because both functions failed. REA's error repeats Kuna's "see the per-function error records" instruction. Its captured stdout says where Kuna wrote the project. 2. Before closing the session, check that reported project path: it no longer exists. Repeat in both compact and full MCP profiles. 3. In the same session, `procedure_pseudo_code` for the function succeeds via a separate standalone decompile, which has different whole-program context. 4. Run the same injected `decompile-project` directly with a caller-owned output directory. Kuna exits 1 but leaves its `.c` output, containing individual `Function ... (error: worker chunk failed ...)` records for both addresses. These are deliberately injected failures, not naturally observed worker crashes. The source-owned target SHA-256 `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930` was unchanged and the target was not executed. ### Expected behavior Do not tell an agent to inspect records that REA has removed. Consider preserving useful bounded per-function failure observations before cleanup, or explicitly say they are unavailable and point to a real recovery path. Preserve cleanup ownership and the provider failure rather than publishing a failed project as complete. Distinguish standalone pseudocode from project-dependent operations. ### Impact and scope The caller receives aggregate captured process diagnostics but cannot follow the provider's instruction to inspect its per-function records. Other failure modes, large error records and other platforms were not exercised; a CLI control is recorded below. The control does not establish frequency or imply that keeping all temporary files is safe or desirable. ### REA version `9ebbe67e05af08b567e215340da993c08dae378f` ### Runtime and platform macOS arm64; Node 24.22.1; Kuna 1.989 behind the described test-only wrapper. ### Target and artifact context Owned, unexecuted arm64 Mach-O with two discovered functions; read-only static analysis. ### Interface MCP ### Earliest failure stage Provider failure projection and cleanup ### Relevant output or evidence Kuna direct output contains `Function: _audit_value @ 0x100000328 (error: worker chunk failed ...)` and the corresponding `_main` record. REA returns the nonzero exit and output diagnostics but the temporary project named in the captured stdout no longer exists before session close. ### Typed outcome or recovery expected Explicitly distinguish complete project, failed project, unavailable failure records and successful standalone fallback. Bound retained data; preserve cancellation and cleanup-retry semantics. ### Analysis or suspected cause Source-derived: `KunaProjectCache` stages exports and retires owned, stopped staging on failure. This is appropriate cleanup ownership; the observed mismatch is passing along an instruction that presupposes a retained provider output. A fix must not assume every failing provider has created a valid project. ### Additional context Related #8167 concerns failures within a completed project; #8740 concerns contradictory standalone-fallback Evidence. cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 ### CLI and producing-boundary control With the same two-function target and deliberate panic injection, a fresh-cache `rea function <target> 0x100000328 --provider kuna --format json` on main `9ebbe67e` exited 1 with `capability_unavailable` for `analyze_function`. The error repeated the instruction to see per-function records and included the temporary project path in captured stdout; that project path did not exist after the command. The target SHA-256 remained `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930`. Source inspection: `exportKunaProject` returns the command failure before it parses the export summary, checks output files, or calls its existing streaming function-block parser. `KunaProjectCache` then retires staging only after the build is proven stopped. A possible partial-diagnostic design would therefore need its own admission and resource bounds for nonzero-exit output, while preserving the original command failure and cleanup/cancellation results; the success-path parser and publication do not automatically establish validity of failed output. ### Partial assembly in the failed direct export The caller-owned direct Kuna 1.989 output from the injected all-worker failure above also contained `arm64.asm`. Its six instruction addresses and mnemonics for `_audit_value` matched a separate direct Kuna `disassemble` query and independent `otool -tvV` on this fixture. The static target digest remained `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930`. In the REA control, `procedure_assembly` still returned project-dependent unavailability after the same injected failure because REA retired the failed staging directory. This demonstrates potentially useful partial output in one deliberately failed project, not that every failed export writes valid or complete assembly, or that a partial project should be published as successful. Direct Kuna disassembly is a separately invoked external workflow, not a demonstrated REA fallback. Source inspection for a bounded partial-diagnostic design: the successful-export `.c` scanner verifies headers against a completed summary, caps each retained structural line at 64 KiB, and accumulates retained header lines before that validation; that per-line cap is not a total record or memory bound. The nonzero-exit path returns before completed-summary parsing. Reusing that scanner on failed output therefore would not by itself establish bounded partial-diagnostic admission or completeness. Keep the primary process failure, incomplete coverage, resource limits and staging ownership explicit, and do not infer that the assembly was validated merely from this control. ### Existing REA provider alternative In fresh compact and full stdio MCP sessions on main `9ebbe67e` with the same deliberate all-worker failure and Kuna 1.989, `procedure_assembly` failed and standalone pseudocode succeeded. `inspect_native_instruction` and `read_bytes` returned `capability_unavailable` because Kuna declares neither operation, with generic recovery. After closing that binary and reopening the same fixture with Ghidra 12.1.4 in the same MCP server, `inspect_native_instruction` decoded the entry as `sub` and `read_bytes` returned the complete first four bytes `ff4300d1` in both profiles. The target digest stayed `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930` and it was not executed. This is one independently available provider switch, not a Kuna fallback, partial-project recovery, or a guarantee that Ghidra is installed or supports another target. Switching requires closing and reopening the binary; preserve any needed session Evidence first. The Kuna session's capability groups did not list the unsupported instruction and byte operations; absence is not proof that the agent discovered an alternative. The fault was injected, not a natural-failure-frequency observation. ### Provider-switch Evidence handoff In fresh-cache compact and full MCP sessions on main `9ebbe67e`, after the deliberately failed Kuna project I exported the successful standalone pseudocode Evidence, closed the binary, reopened the same unexecuted fixture with Ghidra 12.1.4 in the same MCP server, and imported the bundle. `get_evidence_bundle` contained the original Kuna pseudocode record exactly unchanged while the Ghidra instruction and byte queries succeeded as above. The target digest stayed `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930`. This tests one in-process provider switch and explicit bundle handoff, not automatic cross-provider fallback or a fresh-server handoff. `inspect_analysis_view` reported the pseudocode operation unsupported for analysis views; bundle retrieval is the tested path for that record. ### Failure receipt and bundle scope In another fresh-cache real Kuna 1.989 session with the same injected failure, `get_evidence_bundle` immediately after the failed `procedure_assembly` call returned no records in either MCP profile. The error response itself retained the aggregate failure and captured diagnostic details, but had no Evidence ID. After the separate successful pseudocode call, its Evidence could be exported and imported across the Ghidra provider switch as described above. The target digest was unchanged. Therefore this tested bundle handoff preserves the successful observation, not the original failed-call receipt or removed per-function records. This is an observed scope of one failure and does not establish that all REA errors should become Evidence records; any design for preserving failed attempts needs an explicit contract and bounds. ### Failure context within successful fallback Evidence In the provider-switch bundle control, the retained standalone pseudocode record did preserve `raw_result.project_export.state: failed` and the aggregate failure reason in both MCP profiles. That reason repeats Kuna's instruction to see the now-removed per-function records, so it is useful context but not the original error receipt or the individual records. On main `9ebbe67e` its limitations also included the contradictory "still building" wording tracked by #8740. Repeating the same real-provider handoff on #8742 head `d2859517d9071828eb65e42df9b25e3011f6aa2a` removed that timing contradiction, kept the single-function and failed-project caveats, and retained the same type of aggregate failure context; it did not recover the removed records. Both controls used the deliberate worker faults and preserved the target digest `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930`. This qualifies the preceding receipt observation: some failure context is portable when a successful fallback record exists. ### Reopen recovery control On REA `9ebbe67e` with Kuna 1.989, a test-only wrapper injected the same all-worker panic until a marker was removed. In compact and full stdio MCP sessions, `procedure_assembly` failed as above; removing the fault and repeating the call in the same binary session still returned `capability_unavailable`. Closing and reopening the target in the same MCP server then produced assembly successfully. This validates the suggested reopen step for one resolved, injected failure; it does not recover the deleted per-function records or establish recovery from other failure and cleanup modes. The unexecuted target's SHA-256 remained `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930`. ### Explicit record-availability guidance Draft #8789 reports that REA does not expose failed-project per-function records in the project-dependent error and standalone fallback Evidence, while preserving the upstream failure and captured output. A real Kuna 1.989 wrapper-injected all-worker failure reproduced the error and standalone fallback in compact/full MCP and the error in CLI; the target stayed unchanged and was not executed. The change does not recover individual records; the separate bounded partial-diagnostic question remains open. #8742 separately addresses the stale building-state limitation.
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#7531
No description provided.