[GH-ISSUE #6113] [Bug] Evidence-file analysis views replace cancellation with a reasonless error #3745

Closed
opened 2026-10-31 00:43:36 +00:00 by bsakaguchi · 0 comments
Owner

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

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

For an already-aborted evidence-file view request, readEvidenceFileSource returns an AnalysisCancelledError with the exact caller reason. inspectAnalysisViewWithFiles replaces it with a new AnalysisCancelledError whose cause is undefined. A focused test reproduced this for null, a string and an object reason. No file read or descriptor-close failure is required.

This is a production application-boundary reproduction. The MCP registration forwards its request signal to the same service; SDK cancellation and wire delivery were not executed.

Reproduction

Use a prepared checkout with the source revision below and locked dependencies. Save the following as src/application/cancellationReason.repro.test.ts, then run only that file with the repository-pinned Node.js 24.18.0:

node node_modules/vitest/vitest.mjs run src/application/cancellationReason.repro.test.ts --maxWorkers 1

The assertions characterize the current behavior, so a passing test demonstrates the defect and its controls. Remove the test after running it.

import { expect, it } from 'vitest';
import { inspectAnalysisViewWithFiles } from './analysisView/AnalysisViewService.js';
import { readEvidenceFileSource } from './analysisView/evidenceFileSource.js';

it.each([null, 'caller stop', { phase: 'file-read' }])('Evidence view replaces cause-bearing cancellation %j', async reason => {
  const controller = new AbortController(); controller.abort(reason);
  const path = '/targets/unopened-evidence.json';
  const options = { signal: controller.signal };
  const reader = await readEvidenceFileSource(path, options);
  const view = await inspectAnalysisViewWithFiles({ source: { kind: 'evidence-file', path }, view: { kind: 'summary' } }, undefined, options);
  expect(reader.ok).toBe(false); expect(view.ok).toBe(false);
  if (reader.ok || view.ok) throw new Error('Expected cancellation');
  expect(reader.error.cause).toBe(reason);
  expect(view.error._tag).toBe('AnalysisCancelledError');
  expect(view.error.cause).toBeUndefined();
  console.log('VIEW', JSON.stringify({ reason, readerExact: reader.error.cause === reason, viewExact: view.error.cause === reason }));
});

Expected behavior

Preserve the exact caller abort reason in both service cancellation checkpoints, including when the file reader already returned typed cancellation. The lifecycle contract requires exact reason retention. The file-reader guard already meets this contract.

Impact and scope

P2: affected typed-result consumers lose cancellation provenance even though the cancellation category stays correct. Code retaining its signal can still inspect the reason. No changed wire field, damaged Evidence, descriptor leak or incorrect successful projection was observed. The executed case is deterministic for pre-aborted file-source requests; naturally timed late cancellation and post-projection cancellation were not measured.

REA version

rea-agents 6.7.0; source revision 5241597594.

Runtime and platform

macOS 27.0.1 arm64; Node.js v24.18.0 (repository pin), locked Vitest 5.0.3. The initial nine-case run also reproduced on supported Node.js v26.12.0. The final combined focused run passed ten characterization checks on Node v24.18.0.

Target and artifact context

An evidence-file source with a valid absolute selected path and summary view. The signal is already aborted, so the reader does not access that path. No native target or provider is required.

Interface

MCP

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

Observed diagnostics from the focused run:

VIEW {"reason":null,"readerExact":true,"viewExact":false}
VIEW {"reason":"caller stop","readerExact":true,"viewExact":false}
VIEW {"reason":{"phase":"file-read"},"readerExact":true,"viewExact":false}

The tests assert exact reason identity and the reported typed result. They do not assert that an already-cancelled MCP client receives a response.

Typed outcome or recovery expected

AnalysisCancelledError with operation inspect_analysis_view and cause exactly equal to the request signal.reason. Keep cancellation classification and ordinary projection behavior intact.

Analysis or suspected cause

The post-read check runs before loaded.ok is examined, replacing even the reader's correctly attributed cancellation. The post-projection check has the same missing cause, by source inspection; that timing was not separately executed. The MCP registration forwards cancellation into the service.

Additional context

#6077 concerns a failed physical descriptor close escaping the typed boundary. This case needs no descriptor and is fixed at the service cancellation constructors rather than the close owner. #5686 concerns physical JSON read failure precedence, #5968 concerns snapshot cancellation operation attribution while retaining the exact reason, and #5944 concerns runDerivedOperation. PR #5293 introduced file-backed views but does not fix these checkpoints. #4638 and closed PR #4711 concern other producers. The relevant source is unchanged on checked upstream main d8a9673fc4. That newer revision was not executed.

Other cancellation-reason reports cover separate producer boundaries: #5935, #5944, #5959, #5960, #5964, #5985, #5987, #5998, #5999, #6002, #6032, #6037, #6042, #6100, #6101, #6110. These are related provenance defects; this report concerns the evidence-file view service cancellation checkpoints.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6113 ### 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 For an already-aborted evidence-file view request, `readEvidenceFileSource` returns an AnalysisCancelledError with the exact caller reason. `inspectAnalysisViewWithFiles` replaces it with a new AnalysisCancelledError whose cause is undefined. A focused test reproduced this for null, a string and an object reason. No file read or descriptor-close failure is required. This is a production application-boundary reproduction. The MCP registration forwards its request signal to the same service; SDK cancellation and wire delivery were not executed. ### Reproduction Use a prepared checkout with the source revision below and locked dependencies. Save the following as `src/application/cancellationReason.repro.test.ts`, then run only that file with the repository-pinned Node.js 24.18.0: ```sh node node_modules/vitest/vitest.mjs run src/application/cancellationReason.repro.test.ts --maxWorkers 1 ``` The assertions characterize the current behavior, so a passing test demonstrates the defect and its controls. Remove the test after running it. ```ts import { expect, it } from 'vitest'; import { inspectAnalysisViewWithFiles } from './analysisView/AnalysisViewService.js'; import { readEvidenceFileSource } from './analysisView/evidenceFileSource.js'; it.each([null, 'caller stop', { phase: 'file-read' }])('Evidence view replaces cause-bearing cancellation %j', async reason => { const controller = new AbortController(); controller.abort(reason); const path = '/targets/unopened-evidence.json'; const options = { signal: controller.signal }; const reader = await readEvidenceFileSource(path, options); const view = await inspectAnalysisViewWithFiles({ source: { kind: 'evidence-file', path }, view: { kind: 'summary' } }, undefined, options); expect(reader.ok).toBe(false); expect(view.ok).toBe(false); if (reader.ok || view.ok) throw new Error('Expected cancellation'); expect(reader.error.cause).toBe(reason); expect(view.error._tag).toBe('AnalysisCancelledError'); expect(view.error.cause).toBeUndefined(); console.log('VIEW', JSON.stringify({ reason, readerExact: reader.error.cause === reason, viewExact: view.error.cause === reason })); }); ``` ### Expected behavior Preserve the exact caller abort reason in both service cancellation checkpoints, including when the file reader already returned typed cancellation. The [lifecycle contract](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/tool-design.md#L101-L110) requires exact reason retention. The [file-reader guard](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/analysisView/evidenceFileSource.ts#L74-L101) already meets this contract. ### Impact and scope P2: affected typed-result consumers lose cancellation provenance even though the cancellation category stays correct. Code retaining its signal can still inspect the reason. No changed wire field, damaged Evidence, descriptor leak or incorrect successful projection was observed. The executed case is deterministic for pre-aborted file-source requests; naturally timed late cancellation and post-projection cancellation were not measured. ### REA version rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e. ### Runtime and platform macOS 27.0.1 arm64; Node.js v24.18.0 (repository pin), locked Vitest 5.0.3. The initial nine-case run also reproduced on supported Node.js v26.12.0. The final combined focused run passed ten characterization checks on Node v24.18.0. ### Target and artifact context An evidence-file source with a valid absolute selected path and summary view. The signal is already aborted, so the reader does not access that path. No native target or provider is required. ### Interface MCP ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence Observed diagnostics from the focused run: ```text VIEW {"reason":null,"readerExact":true,"viewExact":false} VIEW {"reason":"caller stop","readerExact":true,"viewExact":false} VIEW {"reason":{"phase":"file-read"},"readerExact":true,"viewExact":false} ``` The tests assert exact reason identity and the reported typed result. They do not assert that an already-cancelled MCP client receives a response. ### Typed outcome or recovery expected AnalysisCancelledError with operation inspect_analysis_view and cause exactly equal to the request signal.reason. Keep cancellation classification and ordinary projection behavior intact. ### Analysis or suspected cause The [post-read check](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/analysisView/AnalysisViewService.ts#L93-L101) runs before loaded.ok is examined, replacing even the reader's correctly attributed cancellation. The [post-projection check](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/application/analysisView/AnalysisViewService.ts#L102-L110) has the same missing cause, by source inspection; that timing was not separately executed. The [MCP registration](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/server/registerAnalysisViewTool.ts#L28-L36) forwards cancellation into the service. ### Additional context [#6077](https://github.com/morluto/rea/issues/6077) concerns a failed physical descriptor close escaping the typed boundary. This case needs no descriptor and is fixed at the service cancellation constructors rather than the close owner. #5686 concerns physical JSON read failure precedence, #5968 concerns snapshot cancellation operation attribution while retaining the exact reason, and #5944 concerns runDerivedOperation. PR #5293 introduced file-backed views but does not fix these checkpoints. #4638 and closed PR #4711 concern other producers. The relevant source is unchanged on checked upstream main d8a9673fc44fde4bd30ff98c241886e6683580ca. That newer revision was not executed. Other cancellation-reason reports cover separate producer boundaries: [#5935](https://github.com/morluto/rea/issues/5935), [#5944](https://github.com/morluto/rea/issues/5944), [#5959](https://github.com/morluto/rea/issues/5959), [#5960](https://github.com/morluto/rea/issues/5960), [#5964](https://github.com/morluto/rea/issues/5964), [#5985](https://github.com/morluto/rea/issues/5985), [#5987](https://github.com/morluto/rea/issues/5987), [#5998](https://github.com/morluto/rea/issues/5998), [#5999](https://github.com/morluto/rea/issues/5999), [#6002](https://github.com/morluto/rea/issues/6002), [#6032](https://github.com/morluto/rea/issues/6032), [#6037](https://github.com/morluto/rea/issues/6037), [#6042](https://github.com/morluto/rea/issues/6042), [#6100](https://github.com/morluto/rea/issues/6100), [#6101](https://github.com/morluto/rea/issues/6101), [#6110](https://github.com/morluto/rea/issues/6110). These are related provenance defects; this report concerns the evidence-file view service cancellation checkpoints.
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#3745
No description provided.