[GH-ISSUE #5385] [Bug] LLDB exit failure and no-target proof are lost to cancellation during cleanup #3261

Closed
opened 2026-10-31 00:38:30 +00:00 by bsakaguchi · 1 comment
Owner

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

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • Searched open and closed LLDB cancellation reports and PRs, including #2397, #4009, #4232, #4742, #5058, #5059 and the lifecycle tracker #4227.
  • Only an authored Node peer and temporary runtime files are involved; no private sample or credentials.

Area

Process capture or runtime observation.

Observed behavior

A controlled run through the production default LLDB process runner replaced an already observed bridge failure with cancellation when abort arrived during process cleanup. It also skipped the bridge's explicit no-target-created fact, leaving native-target:unknown cleanup reported even though the peer never launched a target.

The fake LLDB peer wrote a valid target-error result with error fixture physical bridge failure and target_process_created: false, then exited 17. The supervisor had observed {code:17,signal:null} before the test aborted at a held cleanup boundary. The original owned-process cleanup subsequently completed, but the tracer returned AnalysisCancelledError, partial reason cancelled, no bridge error, and unresolved native-target/runtime markers.

Reproduction

Executed using pinned Node24.18.0 and a temporary Vitest boundary test:

  1. Construct LldbCallTracer with its default launch function and a controlled resolver selecting an authored executable Node script as LLDB. No real debugger is selected.
  2. The script accepts the normal rea_trace <config> argument, writes the result above to config.result_path, and exits17. It launches no target.
  3. Spy on the first ProviderProcessSupervisor.stop() call. Record its observed exit and hold before invoking the original stop.
  4. After exit17 is confirmed, abort the caller signal, release the hold and await trace. The original stop must finish without incomplete.
  5. Assert the bridge failure remains primary and no native target cleanup is pending. Current source fails: cancellation and native-target:unknown are returned instead.

This is an executed controlled process/readback-boundary reproduction, not a real LLDB reproduction. The Node peer settled; the test removed its owned fixture-only temporary roots.

Expected behavior

Preserve the already selected exit outcome through cleanup, parse the bridge error and explicit no-target proof, and return the existing typed target-error outcome (capability_unavailable) with its reason. Continue cancelling successful results when cancellation arrives during finalization, and retain actual unresolved cleanup independently.

Impact and scope

The caller loses the failure reason and receives false unresolved-target cleanup state. Frequency on real debugger sessions is NOT_ENOUGH_INFO; the timing was controlled with a barrier. No actual LLDB, debugserver, debuggee, or CLI/MCP client was launched. A thrown cleanup failure and deadline-only interruption were not part of this reproduction.

REA version

Local source799e8265b254e225dc1dabdd25e56f0ab7f86bd3 (6.7.0) with unrelated local changes. The LLDB tracer file is unchanged from that commit and byte-identical to remote main074768901f3cdc8205b61773fb1faf6f6c7b3ca8. That remote revision was source-compared, not separately executed.

Runtime and platform

Node24.18.0, Darwin25.3.0 arm64. The executable peer is Node, not installed LLDB.

Target and artifact context

Authored fake LLDB executable and JSON result only; no target process.

Interface / earliest failure stage

Production process/adapter boundary; cleanup, cancellation, or lifecycle.

Relevant output and source

Reduced observed result:

{
  "error": "AnalysisCancelledError",
  "partial_reason": "cancelled",
  "cleanup_resources": ["native-target:unknown", "<owned runtime root>"]
}

runLldb.finish awaits stop, then replaces the supplied outcome using the deadline's later interruption state. The normal exit path already snapshotted exit/output before calling it. The readback owner parses the result only for an exited outcome, so replacement also drops the no-target fact.

#4009 concerns the opposite requirement: successful observations must become cancellation during finalization. Its outer success-only guard remains necessary. #4232 concerns bridge exit proof after denied launch. This counterexample concerns a physical failure already observed before cleanup, not a real launch-identity failure or the #2397 verifier investigation. No production fix is claimed here.

Originally created by @morluto on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5385 Originally assigned to: @kaoru0822-kitauji on GitHub. ## Before submitting - [x] Searched open and closed LLDB cancellation reports and PRs, including #2397, #4009, #4232, #4742, #5058, #5059 and the lifecycle tracker #4227. - [x] Only an authored Node peer and temporary runtime files are involved; no private sample or credentials. ## Area Process capture or runtime observation. ## Observed behavior A controlled run through the production default LLDB process runner replaced an already observed bridge failure with cancellation when abort arrived during process cleanup. It also skipped the bridge's explicit no-target-created fact, leaving `native-target:unknown` cleanup reported even though the peer never launched a target. The fake LLDB peer wrote a valid `target-error` result with error `fixture physical bridge failure` and `target_process_created: false`, then exited 17. The supervisor had observed `{code:17,signal:null}` before the test aborted at a held cleanup boundary. The original owned-process cleanup subsequently completed, but the tracer returned `AnalysisCancelledError`, partial reason `cancelled`, no bridge error, and unresolved native-target/runtime markers. ## Reproduction Executed using pinned Node24.18.0 and a temporary Vitest boundary test: 1. Construct `LldbCallTracer` with its default launch function and a controlled resolver selecting an authored executable Node script as LLDB. No real debugger is selected. 2. The script accepts the normal `rea_trace <config>` argument, writes the result above to `config.result_path`, and exits17. It launches no target. 3. Spy on the first `ProviderProcessSupervisor.stop()` call. Record its observed exit and hold before invoking the original stop. 4. After exit17 is confirmed, abort the caller signal, release the hold and await trace. The original stop must finish without `incomplete`. 5. Assert the bridge failure remains primary and no native target cleanup is pending. Current source fails: cancellation and `native-target:unknown` are returned instead. This is an executed controlled process/readback-boundary reproduction, not a real LLDB reproduction. The Node peer settled; the test removed its owned fixture-only temporary roots. ## Expected behavior Preserve the already selected exit outcome through cleanup, parse the bridge error and explicit no-target proof, and return the existing typed target-error outcome (`capability_unavailable`) with its reason. Continue cancelling successful results when cancellation arrives during finalization, and retain actual unresolved cleanup independently. ## Impact and scope The caller loses the failure reason and receives false unresolved-target cleanup state. Frequency on real debugger sessions is NOT_ENOUGH_INFO; the timing was controlled with a barrier. No actual LLDB, debugserver, debuggee, or CLI/MCP client was launched. A thrown cleanup failure and deadline-only interruption were not part of this reproduction. ## REA version Local source799e8265b254e225dc1dabdd25e56f0ab7f86bd3 (6.7.0) with unrelated local changes. The LLDB tracer file is unchanged from that commit and byte-identical to remote main074768901f3cdc8205b61773fb1faf6f6c7b3ca8. That remote revision was source-compared, not separately executed. ## Runtime and platform Node24.18.0, Darwin25.3.0 arm64. The executable peer is Node, not installed LLDB. ## Target and artifact context Authored fake LLDB executable and JSON result only; no target process. ## Interface / earliest failure stage Production process/adapter boundary; cleanup, cancellation, or lifecycle. ## Relevant output and source Reduced observed result: ```json { "error": "AnalysisCancelledError", "partial_reason": "cancelled", "cleanup_resources": ["native-target:unknown", "<owned runtime root>"] } ``` [`runLldb.finish`](https://github.com/morluto/rea/blob/074768901f3cdc8205b61773fb1faf6f6c7b3ca8/src/native/LldbCallTracer.ts#L1096-L1112) awaits stop, then replaces the supplied outcome using the deadline's later interruption state. The [normal exit path](https://github.com/morluto/rea/blob/074768901f3cdc8205b61773fb1faf6f6c7b3ca8/src/native/LldbCallTracer.ts#L1155-L1167) already snapshotted exit/output before calling it. The [readback owner](https://github.com/morluto/rea/blob/074768901f3cdc8205b61773fb1faf6f6c7b3ca8/src/native/LldbCallTracer.ts#L507-L539) parses the result only for an exited outcome, so replacement also drops the no-target fact. ## Related work #4009 concerns the opposite requirement: successful observations must become cancellation during finalization. Its outer success-only guard remains necessary. #4232 concerns bridge exit proof after denied launch. This counterexample concerns a physical failure already observed before cleanup, not a real launch-identity failure or the #2397 verifier investigation. No production fix is claimed here.
Author
Owner

@XiaoBinGan commented on GitHub (Oct 21, 2026):

Verified on a5f982de8 that the exit-failure half of this report is already fixed: runLldb's finish() selects processFailureBeforeInterruption when the outcome is not exited ("An exit observed before interruption outranks an interrupted wait"), and a controlled repro — fake bridge peer that exits non-zero, then setImmediate abort during cleanup — returns the bridge target-error rather than AnalysisCancelledError. I did not verify the separate no-target-proof half.

<!-- gh-comment-id:6312046826 --> @XiaoBinGan commented on GitHub (Oct 21, 2026): Verified on `a5f982de8` that the exit-failure half of this report is already fixed: `runLldb`'s `finish()` selects `processFailureBeforeInterruption` when the outcome is not `exited` ("An exit observed before interruption outranks an interrupted wait"), and a controlled repro — fake bridge peer that exits non-zero, then `setImmediate` abort during cleanup — returns the bridge target-error rather than `AnalysisCancelledError`. I did not verify the separate no-target-proof half.
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#3261
No description provided.