[GH-ISSUE #5373] [Bug] GDB startup cancellation drops the caller's exact abort reason #3258

Closed
opened 2026-10-31 00:38:29 +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/5373

Before submitting

  • I searched the open and closed issues and related PRs.
  • I removed credentials, private paths, and sensitive target data.

Area

Process capture or runtime observation

Observed behavior

GdbSessionManager.start(signal) returns AnalysisCancelledError without preserving the caller's exact abort reason. For an already-aborted signal, error.cause is undefined and no spawner is called. In a controlled post-entry startup cancellation, cause is an internal AnalysisCancelledError, not the original object.

These are in-process typed-result observations. No changed CLI/MCP payload, real GDB behavior, target execution, or OS-process lifetime claim is made.

Reproduction

On source 799e8265b254e225dc1dabdd25e56f0ab7f86bd3 with locked dependencies and Node 24.18.0, run from the checkout:

node --import tsx --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { GdbSessionManager } from './src/gdb/GdbSessionManager.ts';
let spawns = 0;
const manager = new GdbSessionManager({
  platform: 'linux',
  environment: { REA_GDB_COMMAND: 'unused' },
  spawnProcess: async () => { spawns++; throw new Error('must not spawn'); },
});
const controller = new AbortController();
const reason = Object.freeze({ requested: 'stop' });
controller.abort(reason);
const result = await manager.start(controller.signal);
assert.equal(result.ok, false);
if (result.ok) throw new Error('Expected cancellation');
console.log(JSON.stringify({ tag: result.error._tag,
  exactCause: result.error.cause === reason,
  causeAbsent: result.error.cause === undefined, spawns }));
assert.equal(result.error.cause, reason);
JS

I executed this minimal reproduction once at that exact clean revision: it exits 1 on the final assertion. A larger controlled source probe reproduced the pre-aborted and post-entry cases both on that revision and on a local checkout carrying the separate #5331 readiness repair. That probe injects an EventEmitter/PassThrough fake through the spawner seam; it never spawns an OS process or debugger.

Expected behavior

The cancellation contract requires the exact caller abort reason as the typed cancellation error's cause, while keeping physical primary failures and cleanup ownership separate. Expected result.error.cause === reason, with the cancellation tag and no-spawn behavior preserved.

Impact and scope

Observed: typed in-process callers cannot recover their caller-supplied cancellation metadata from cause. The operation still cancels. In the post-entry synthetic case, an incomplete-stop reason/resource remains separately reported and retryable. A consumed MI configuration failure remained the physical primary error when a later abort was observed in the controlled ordering check.

NOT_ENOUGH_INFO: external client impact, natural timing frequency, real GDB/Linux behavior, active-console runtime reproduction, and the first affected release. Source also shows missing reason propagation in queue waits and active MI abort branches, but those are not runtime-proven by this report.

REA version

Package 6.7.0; exact source 799e8265b254e225dc1dabdd25e56f0ab7f86bd3.

Runtime and platform

Node 24.18.0, macOS/Darwin arm64. platform: 'linux' is an injected admission option; this is not Linux verification.

Target and artifact context

No target. The minimal case never calls the spawner. The additional probe uses only an in-memory fake MI process object.

Interface

Other — in-process production manager

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

Minimal executed output:

{"tag":"AnalysisCancelledError","exactCause":false,"causeAbsent":true,"spawns":0}

The final strict-equality assertion fails because the cause is undefined. In the additional post-entry probe: cancellation tag, caller-cause identity false, cause tag AnalysisCancelledError; retained fake owner and cleanup resource remained separate until retry.

Typed outcome or recovery expected

Preserve the exact opaque caller reason as data. Do not interpret a reason's type/properties as cleanup authority, and do not replace an earlier consumed physical failure with late cancellation.

Analysis or suspected cause

Source-derived: pre-aborted start constructs cause-less cancellation. Startup listeners/checkpoints similarly omit signal.reason, and the catch wraps that internal error. Provider shutdown's internal abort remains a distinct case; blindly replacing every cause with the ambient signal is not a proposed fix.

Additional context

Related #5331/#5335 concern failed-start readiness and cleanup retry; #5332 is the analogous EVM artifact projection issue; #4227/#4330 are broad trackers. Open PR #1961 touches GDB interrupt/cleanup ownership, but its reviewed hunks do not carry caller reasons through startup/queue/MI aborts. No standalone GDB exact-reason report was found in open/closed searches. No implementation is included here. AI-assisted report; the source probes above were executed as stated.

Originally created by @morluto on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5373 ### Before submitting - [x] I searched the open and closed issues and related PRs. - [x] I removed credentials, private paths, and sensitive target data. ### Area Process capture or runtime observation ### Observed behavior `GdbSessionManager.start(signal)` returns `AnalysisCancelledError` without preserving the caller's exact abort reason. For an already-aborted signal, `error.cause` is `undefined` and no spawner is called. In a controlled post-entry startup cancellation, `cause` is an internal `AnalysisCancelledError`, not the original object. These are in-process typed-result observations. No changed CLI/MCP payload, real GDB behavior, target execution, or OS-process lifetime claim is made. ### Reproduction On source `799e8265b254e225dc1dabdd25e56f0ab7f86bd3` with locked dependencies and Node 24.18.0, run from the checkout: ```sh node --import tsx --input-type=module <<'JS' import assert from 'node:assert/strict'; import { GdbSessionManager } from './src/gdb/GdbSessionManager.ts'; let spawns = 0; const manager = new GdbSessionManager({ platform: 'linux', environment: { REA_GDB_COMMAND: 'unused' }, spawnProcess: async () => { spawns++; throw new Error('must not spawn'); }, }); const controller = new AbortController(); const reason = Object.freeze({ requested: 'stop' }); controller.abort(reason); const result = await manager.start(controller.signal); assert.equal(result.ok, false); if (result.ok) throw new Error('Expected cancellation'); console.log(JSON.stringify({ tag: result.error._tag, exactCause: result.error.cause === reason, causeAbsent: result.error.cause === undefined, spawns })); assert.equal(result.error.cause, reason); JS ``` I executed this minimal reproduction once at that exact clean revision: it exits 1 on the final assertion. A larger controlled source probe reproduced the pre-aborted and post-entry cases both on that revision and on a local checkout carrying the separate #5331 readiness repair. That probe injects an EventEmitter/PassThrough fake through the spawner seam; it never spawns an OS process or debugger. ### Expected behavior [The cancellation contract](https://github.com/morluto/rea/blob/799e8265b254e225dc1dabdd25e56f0ab7f86bd3/docs/tool-design.md#L102-L114) requires the exact caller abort reason as the typed cancellation error's cause, while keeping physical primary failures and cleanup ownership separate. Expected `result.error.cause === reason`, with the cancellation tag and no-spawn behavior preserved. ### Impact and scope Observed: typed in-process callers cannot recover their caller-supplied cancellation metadata from `cause`. The operation still cancels. In the post-entry synthetic case, an incomplete-stop reason/resource remains separately reported and retryable. A consumed MI configuration failure remained the physical primary error when a later abort was observed in the controlled ordering check. NOT_ENOUGH_INFO: external client impact, natural timing frequency, real GDB/Linux behavior, active-console runtime reproduction, and the first affected release. Source also shows missing reason propagation in queue waits and active MI abort branches, but those are not runtime-proven by this report. ### REA version Package 6.7.0; exact source `799e8265b254e225dc1dabdd25e56f0ab7f86bd3`. ### Runtime and platform Node 24.18.0, macOS/Darwin arm64. `platform: 'linux'` is an injected admission option; this is not Linux verification. ### Target and artifact context No target. The minimal case never calls the spawner. The additional probe uses only an in-memory fake MI process object. ### Interface Other — in-process production manager ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence Minimal executed output: ```json {"tag":"AnalysisCancelledError","exactCause":false,"causeAbsent":true,"spawns":0} ``` The final strict-equality assertion fails because the cause is `undefined`. In the additional post-entry probe: cancellation tag, caller-cause identity false, cause tag `AnalysisCancelledError`; retained fake owner and cleanup resource remained separate until retry. ### Typed outcome or recovery expected Preserve the exact opaque caller reason as data. Do not interpret a reason's type/properties as cleanup authority, and do not replace an earlier consumed physical failure with late cancellation. ### Analysis or suspected cause Source-derived: [pre-aborted start](https://github.com/morluto/rea/blob/799e8265b254e225dc1dabdd25e56f0ab7f86bd3/src/gdb/GdbSessionManager.ts#L574-L575) constructs cause-less cancellation. Startup listeners/checkpoints similarly omit `signal.reason`, and the catch wraps that internal error. Provider shutdown's internal abort remains a distinct case; blindly replacing every cause with the ambient signal is not a proposed fix. ### Additional context Related #5331/#5335 concern failed-start readiness and cleanup retry; #5332 is the analogous EVM artifact projection issue; #4227/#4330 are broad trackers. Open PR #1961 touches GDB interrupt/cleanup ownership, but its reviewed hunks do not carry caller reasons through startup/queue/MI aborts. No standalone GDB exact-reason report was found in open/closed searches. No implementation is included here. AI-assisted report; the source probes above were executed as stated.
bsakaguchi 2026-10-31 00:38:29 +00:00
  • closed this issue
  • added the
    triage
    bug
    labels
Author
Owner

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

Closing as completed: verified fixed on latest main during REA Bot open-bug audit. A pre-aborted GdbSessionManager.start probe returned the exact abort reason and did not spawn. No further PR needed for this report.

<!-- gh-comment-id:6300557282 --> @morluto commented on GitHub (Oct 20, 2026): Closing as completed: verified fixed on latest main during REA Bot open-bug audit. A pre-aborted `GdbSessionManager.start` probe returned the exact abort reason and did not spawn. No further PR needed for this report.
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#3258
No description provided.