[GH-ISSUE #6658] [Bug] BEAM service admission drops the caller's cancellation reason #4036

Closed
opened 2026-10-31 00:46:15 +00:00 by bsakaguchi · 0 comments
Owner

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

[Bug] BEAM service admission drops the caller's cancellation reason

Labels: bug, triage

Before submitting

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

For an already-aborted signal, createBeamService().inspect({ path }, { signal }) returns AnalysisCancelledError with cause === undefined, discarding the caller's reason. Calling the BEAM provider directly with the same signal preserves the exact reason. Four controls using an object, string, null, and Error all showed this difference.

Reproduction

Executed against the source revision below using the repository’s tsx source runner. The selected file was the repository's compiler-produced erlangBeamOtp29Fixture(), written to a benign temporary .beam file. Core calls from that executed probe, with source imports shown repository-relative:

import { createBeamService } from './src/composition/erlang.ts';
import { BeamProvider } from './src/erlang/BeamProvider.ts';
import { inspectErlangBeamInputSchema } from './src/domain/erlang/beam.ts';

const reason = { source: 'metadata-request', action: 'stop' };
const signal = AbortSignal.abort(reason);
const service = createBeamService();
const provider = new BeamProvider();
const admitted = await service.inspect({ path }, { signal });
const direct = await provider.inspect(
  inspectErlangBeamInputSchema.parse({ path }), { signal },
);
// Both return ok: false and AnalysisCancelledError.
// admitted.error.cause === reason: false (cause is undefined)
// direct.error.cause === reason: true
await service.close();
await provider.close();

The same calls were executed once each for all four reason types. No provider process or target bytecode was run. A separate successful inspection of the original fixture confirmed its module and exports before this probe.

Expected behavior

Preserve signal.reason by identity as the cancellation error's cause, consistently with the BEAM provider and the Go service's admission guard. Cancellation should still stop admission before file reading.

Impact and scope

The shared BEAM application boundary loses cancellation provenance for every already-aborted request with an explicit reason. A caller receiving only the typed error cannot recover whether a deadline, request cancellation, or another caller condition caused the stop. Cancellation itself still works. Observed scope is the application service; CLI/MCP transport consequences and real-world frequency were not tested. This is a bounded provenance defect, assessed P2.

REA version

main revision 23def04096869b85c4d3c14a73ed09168483c9f6; package manifest version 6.7.0. The affected service is absent from the fetched published release tag rea-agents-6.7.0; this report makes no claim that the published package is affected.

Runtime and platform

Node.js v26.12.1; Linux 7.2.8-9-omarchy, x86_64. No external analysis provider or Erlang runtime was involved.

Target and artifact context

Compiler-produced OTP 29 BEAM fixture from src/erlang/Beam.fixture.ts. Failure occurs at service admission, before artifact contents matter.

Interface

Other: shared application service used by CLI/MCP.

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

Captured results:

reason   service tag             service cause  retains reason  direct provider retains reason
object   AnalysisCancelledError  undefined      false           true
string   AnalysisCancelledError  undefined      false           true
null     AnalysisCancelledError  undefined      false           true
Error    AnalysisCancelledError  undefined      false           true

Evidence provenance: executed source-module application/provider controls. Local verification: all four service cases lost the reason; all four provider controls retained it. Reproduction completeness: complete for pre-aborted service admission; public adapters and cancellation arriving later were not exercised.

Typed outcome or recovery expected

AnalysisCancelledError for inspect_erlang_beam, with cause === signal.reason.

Analysis or suspected cause

Source-derived cause: the admission guard constructs the error without options, bypassing the provider's reason-preserving cancellation mapping.

Additional context

Related closed #4929 fixed this contract at Rust, Go, and PE service admission. Its explicit repair scope did not include the subsequently added BEAM service. Searches of open and closed BEAM/Erlang cancellation reports found no report for this service. The older BEAM chunk/atom reports #6503, #6517, and #6518 concern other behavior already corrected in this revision.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Four ordinary explicit cancellation reasons were discarded at the shared BEAM service boundary, although the provider preserved each exact reason; this loses typed cancellation provenance without preventing cancellation itself.
  • Workaround status: unverified
  • Workaround basis: A caller could separately retain its signal reason, but a complete CLI/MCP caller recovery workflow was not exercised. Calling the provider directly preserved the reason in the probe but does not provide the service's Evidence workflow.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6658 # [Bug] BEAM service admission drops the caller's cancellation reason Labels: `bug`, `triage` ### 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 Evidence, artifacts, or reconstruction ### Observed behavior For an already-aborted signal, `createBeamService().inspect({ path }, { signal })` returns `AnalysisCancelledError` with `cause === undefined`, discarding the caller's reason. Calling the BEAM provider directly with the same signal preserves the exact reason. Four controls using an object, string, `null`, and `Error` all showed this difference. ### Reproduction Executed against the source revision below using the repository’s `tsx` source runner. The selected file was the repository's compiler-produced `erlangBeamOtp29Fixture()`, written to a benign temporary `.beam` file. Core calls from that executed probe, with source imports shown repository-relative: ```ts import { createBeamService } from './src/composition/erlang.ts'; import { BeamProvider } from './src/erlang/BeamProvider.ts'; import { inspectErlangBeamInputSchema } from './src/domain/erlang/beam.ts'; const reason = { source: 'metadata-request', action: 'stop' }; const signal = AbortSignal.abort(reason); const service = createBeamService(); const provider = new BeamProvider(); const admitted = await service.inspect({ path }, { signal }); const direct = await provider.inspect( inspectErlangBeamInputSchema.parse({ path }), { signal }, ); // Both return ok: false and AnalysisCancelledError. // admitted.error.cause === reason: false (cause is undefined) // direct.error.cause === reason: true await service.close(); await provider.close(); ``` The same calls were executed once each for all four reason types. No provider process or target bytecode was run. A separate successful inspection of the original fixture confirmed its module and exports before this probe. ### Expected behavior Preserve `signal.reason` by identity as the cancellation error's `cause`, consistently with [the BEAM provider](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/erlang/BeamProvider.ts#L125-L128) and [the Go service's admission guard](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/application/go/GoBinaryService.ts#L44-L47). Cancellation should still stop admission before file reading. ### Impact and scope The shared BEAM application boundary loses cancellation provenance for every already-aborted request with an explicit reason. A caller receiving only the typed error cannot recover whether a deadline, request cancellation, or another caller condition caused the stop. Cancellation itself still works. Observed scope is the application service; CLI/MCP transport consequences and real-world frequency were not tested. This is a bounded provenance defect, assessed P2. ### REA version `main` revision `23def04096869b85c4d3c14a73ed09168483c9f6`; package manifest version `6.7.0`. The affected service is absent from the fetched published release tag `rea-agents-6.7.0`; this report makes no claim that the published package is affected. ### Runtime and platform Node.js `v26.12.1`; Linux `7.2.8-9-omarchy`, x86_64. No external analysis provider or Erlang runtime was involved. ### Target and artifact context Compiler-produced OTP 29 BEAM fixture from `src/erlang/Beam.fixture.ts`. Failure occurs at service admission, before artifact contents matter. ### Interface Other: shared application service used by CLI/MCP. ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence Captured results: ```text reason service tag service cause retains reason direct provider retains reason object AnalysisCancelledError undefined false true string AnalysisCancelledError undefined false true null AnalysisCancelledError undefined false true Error AnalysisCancelledError undefined false true ``` Evidence provenance: executed source-module application/provider controls. Local verification: all four service cases lost the reason; all four provider controls retained it. Reproduction completeness: complete for pre-aborted service admission; public adapters and cancellation arriving later were not exercised. ### Typed outcome or recovery expected `AnalysisCancelledError` for `inspect_erlang_beam`, with `cause === signal.reason`. ### Analysis or suspected cause Source-derived cause: [the admission guard constructs the error without options](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/application/erlang/BeamService.ts#L32-L33), bypassing the provider's reason-preserving cancellation mapping. ### Additional context Related closed [#4929](https://github.com/morluto/rea/issues/4929) fixed this contract at Rust, Go, and PE service admission. Its explicit repair scope did not include the subsequently added BEAM service. Searches of open and closed BEAM/Erlang cancellation reports found no report for this service. The older BEAM chunk/atom reports #6503, #6517, and #6518 concern other behavior already corrected in this revision. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Four ordinary explicit cancellation reasons were discarded at the shared BEAM service boundary, although the provider preserved each exact reason; this loses typed cancellation provenance without preventing cancellation itself. - Workaround status: unverified - Workaround basis: A caller could separately retain its signal reason, but a complete CLI/MCP caller recovery workflow was not exercised. Calling the provider directly preserved the reason in the probe but does not provide the service's Evidence workflow.
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#4036
No description provided.