[GH-ISSUE #5419] [Bug] Web bundle worker replaces observed failures with later cancellation #3285

Closed
opened 2026-10-31 00:38:46 +00:00 by bsakaguchi · 2 comments
Owner

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

Before submitting

  • I searched open/closed reports and related PRs. #5393 covers the separate JavaScriptAnalysisWorker owner; #5292/#5296 cover runOwnedCommand. #5174 concerns WebBundle result-acceptance deadlines.
  • This report contains no credentials, private paths, or sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

WebBundleAnalysisWorker.analyze returns AnalysisCancelledError without captured output when the caller aborts after its owned child has exited 7. The paired no-abort control returns AnalysisOutputError with exit 7 and stderr. Two direct source-boundary runs reproduced this ordering using real owned Node peers and inert input.

Reproduction

Executed on source c284168c4b1cd214ce7f504d43da28528d82d603:

  1. Construct production WebBundleAnalysisWorker(128, launcher) using its existing launcher seam. Have the launcher call spawnOwnedProviderProcess with an authored Node peer.
  2. The peer writes a valid ready frame with getHeapStatistics().heap_size_limit, accepts one script request, writes controlled inert peer exited 7 to stderr, and exits 7 from the write callback. It never executes the supplied script.
  3. From a parent listener on that child's exit, queue controller.abort(opaqueReason) with queueMicrotask. All synchronous exit listeners, including the production supervisor's observation, finish before this abort.
  4. Await worker.analyze(script, 15000, controller.signal). Compare with an otherwise identical run without abort. Close the worker and await pending cleanup in finally.

The independent rerun completed normally with cleanup errors unsuppressed. Broader customer frequency is NOT_ENOUGH_INFO.

Expected behavior

Retain the physical failure observed before interruption as primary and preserve available diagnostics. Abort observed first must remain cancellation. Preserve exact opaque abort metadata and the existing cleanup owner/retry semantics.

Impact and scope

The later abort hides the earlier failed exit and its output. Additional paired controls reproduce cancellation replacing a latched malformed frame and a parsed failure frame. Authored OOM-marker stderr and 300 KiB stderr controls lose their memory/truncation diagnostics after post-exit abort. The OOM marker is text classification only, not actual memory exhaustion.

REA version

Executed at source c284168c4 (package 6.7.0). Relevant worker/runtime source and tests match live main 1417bf943bf49e49b414e8a508bf28a31304be83 byte-for-byte; that immutable revision was source-checked, not separately executed.

Runtime and platform

Node.js 24.18.0; macOS 26.3 arm64.

Target and artifact context

Inert script artifact and owned local Node protocol peer. No target execution, debugger, or real browser workload.

Interface

Other: direct production worker boundary.

Earliest failure stage

Cleanup, cancellation, or lifecycle.

Relevant output or evidence

No abort: AnalysisOutputError; capturedOutput.exit_code = 7; stderr retained
Abort queued after exit observation: AnalysisCancelledError; capturedOutput absent

Source-derived cause: the worker checks interruption before its stored protocol failure, stdin failure and supervisor exit in #next; its outer catch also selects interruption ahead of physical failures. It uses its own OwnedWorkerRuntime/supervisor path, so the runOwnedCommand repair does not cover it.

Typed outcome or recovery expected

The previously observed typed worker failure with available captured output. Cleanup uncertainty must retain the exact owner and remain retryable.

Additional context

Found by auditing nearby consumers after #5393. No public CLI/MCP semantic-analysis reproduction, stdin EPIPE injection, actual OS/V8 exhaustion, or cleanup-refusal injection was performed. No fix is integrated yet.

Originally created by @morluto on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5419 ### Before submitting - [x] I searched open/closed reports and related PRs. #5393 covers the separate JavaScriptAnalysisWorker owner; #5292/#5296 cover runOwnedCommand. #5174 concerns WebBundle result-acceptance deadlines. - [x] This report contains no credentials, private paths, or sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `WebBundleAnalysisWorker.analyze` returns `AnalysisCancelledError` without captured output when the caller aborts after its owned child has exited 7. The paired no-abort control returns `AnalysisOutputError` with exit 7 and stderr. Two direct source-boundary runs reproduced this ordering using real owned Node peers and inert input. ### Reproduction Executed on source `c284168c4b1cd214ce7f504d43da28528d82d603`: 1. Construct production `WebBundleAnalysisWorker(128, launcher)` using its existing launcher seam. Have the launcher call `spawnOwnedProviderProcess` with an authored Node peer. 2. The peer writes a valid ready frame with `getHeapStatistics().heap_size_limit`, accepts one script request, writes `controlled inert peer exited 7` to stderr, and exits 7 from the write callback. It never executes the supplied script. 3. From a parent listener on that child's exit, queue `controller.abort(opaqueReason)` with `queueMicrotask`. All synchronous exit listeners, including the production supervisor's observation, finish before this abort. 4. Await `worker.analyze(script, 15000, controller.signal)`. Compare with an otherwise identical run without abort. Close the worker and await pending cleanup in `finally`. The independent rerun completed normally with cleanup errors unsuppressed. Broader customer frequency is NOT_ENOUGH_INFO. ### Expected behavior Retain the physical failure observed before interruption as primary and preserve available diagnostics. Abort observed first must remain cancellation. Preserve exact opaque abort metadata and the existing cleanup owner/retry semantics. ### Impact and scope The later abort hides the earlier failed exit and its output. Additional paired controls reproduce cancellation replacing a latched malformed frame and a parsed failure frame. Authored OOM-marker stderr and 300 KiB stderr controls lose their memory/truncation diagnostics after post-exit abort. The OOM marker is text classification only, not actual memory exhaustion. ### REA version Executed at source c284168c4 (package 6.7.0). Relevant worker/runtime source and tests match live main `1417bf943bf49e49b414e8a508bf28a31304be83` byte-for-byte; that immutable revision was source-checked, not separately executed. ### Runtime and platform Node.js 24.18.0; macOS 26.3 arm64. ### Target and artifact context Inert script artifact and owned local Node protocol peer. No target execution, debugger, or real browser workload. ### Interface Other: direct production worker boundary. ### Earliest failure stage Cleanup, cancellation, or lifecycle. ### Relevant output or evidence ```text No abort: AnalysisOutputError; capturedOutput.exit_code = 7; stderr retained Abort queued after exit observation: AnalysisCancelledError; capturedOutput absent ``` Source-derived cause: [the worker](https://github.com/morluto/rea/blob/1417bf943bf49e49b414e8a508bf28a31304be83/src/browser/WebBundleAnalysisWorker.ts) checks interruption before its stored protocol failure, stdin failure and supervisor exit in `#next`; its outer catch also selects interruption ahead of physical failures. It uses its own OwnedWorkerRuntime/supervisor path, so the runOwnedCommand repair does not cover it. ### Typed outcome or recovery expected The previously observed typed worker failure with available captured output. Cleanup uncertainty must retain the exact owner and remain retryable. ### Additional context Found by auditing nearby consumers after #5393. No public CLI/MCP semantic-analysis reproduction, stdin EPIPE injection, actual OS/V8 exhaustion, or cleanup-refusal injection was performed. No fix is integrated yet.
bsakaguchi 2026-10-31 00:38:46 +00:00
  • closed this issue
  • added the
    triage
    bug
    labels
Author
Owner

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

Hi @morluto — I prepared a focused candidate fix for #5419 on a dedicated branch in my fork:

Branch: https://github.com/Abdulrahman3M/REA/tree/fix/web-bundle-observed-failures-5419

The proposed change latches the first observed worker failure (failed child exit, malformed/failed protocol frame, input stream failure) before a later caller abort can replace it. It preserves the bounded stderr/exit diagnostics and retains the later opaque cancellation reason as a secondary failure. I also added real-child regression cases covering exit code 7, a text-only OOM marker, truncated stderr, and control-frame failures.

Verification status: This is a candidate patch, not a verified fix. I have reviewed the source and committed tests, but have not run the focused tests or full checks, nor independently reproduced it on your Node 24.18.0/macOS environment. No PR has been opened yet.

Would you be able to try the branch against your original controlled reproduction when convenient and share the actual outcome? In particular, does an abort queued after observed exit 7 now preserve AnalysisOutputError, capturedOutput.exit_code = 7, and stderr? Do malformed/failure frames and the OOM-marker/truncation controls also retain their typed primary error? Any failing test output would be very helpful.

Suggested local checks:

npm ci
npm run test:focused -- src/browser/WebBundleAnalysisWorker.test.ts src/browser/WebBundleAnalysisWorkerReadCompletion.test.ts
npm run check:fast

Once the behavior is confirmed and validation gaps are addressed, I'll prepare a focused pull request. Thanks for the detailed reproduction and any feedback you can share.

<!-- gh-comment-id:6288997559 --> @Abdulrahman3M commented on GitHub (Oct 20, 2026): Hi @morluto — I prepared a focused candidate fix for #5419 on a dedicated branch in my fork: **Branch:** https://github.com/Abdulrahman3M/REA/tree/fix/web-bundle-observed-failures-5419 The proposed change latches the first observed worker failure (failed child exit, malformed/failed protocol frame, input stream failure) before a later caller abort can replace it. It preserves the bounded stderr/exit diagnostics and retains the later opaque cancellation reason as a secondary failure. I also added real-child regression cases covering exit code 7, a text-only OOM marker, truncated stderr, and control-frame failures. **Verification status:** This is a *candidate patch*, not a verified fix. I have reviewed the source and committed tests, but **have not run the focused tests or full checks**, nor independently reproduced it on your Node 24.18.0/macOS environment. No PR has been opened yet. Would you be able to try the branch against your original controlled reproduction when convenient and share the actual outcome? In particular, does an abort queued **after** observed exit 7 now preserve `AnalysisOutputError`, `capturedOutput.exit_code = 7`, and stderr? Do malformed/failure frames and the OOM-marker/truncation controls also retain their typed primary error? Any failing test output would be very helpful. Suggested local checks: ```bash npm ci npm run test:focused -- src/browser/WebBundleAnalysisWorker.test.ts src/browser/WebBundleAnalysisWorkerReadCompletion.test.ts npm run check:fast ``` Once the behavior is confirmed and validation gaps are addressed, I'll prepare a focused pull request. Thanks for the detailed reproduction and any feedback you can share.
Author
Owner

@gensokyo-dev commented on GitHub (Oct 20, 2026):

Independently verified as fixed on main 8716b5241125d81f1a16808eb5178fdfdea1ecdb by maintainer commit cac12ae33. No additional production patch is needed.

Linux x64 / Node 24.18.0 qualification:

  • Five selected real-owned-peer cases reproduce the report on pre-fix 861c11ec7: failure frame, exit 7, malformed frame, oversized-stderr exit and resource failure are replaced by later cancellation. Current main passes 19 worker tests; the separate deliberate heap-exhaustion test was excluded. Failure-first keeps typed diagnostics and opaque secondary abort; abort-first remains cancellation, including explicit null. Existing cleanup-retry and late-output completeness checks pass.
  • The public MCP timeout/Evidence/recovery boundary passes. Real Chrome 156.0.8078.4 public CLI and pinned MCP 2026-07-28 analyze three captured scripts, including unchanged fast-deep-equal 3.1.3 (index.js, 1,177 bytes, SHA-256 eb469e206280321a3878f2335ec98aa2104a155079d8ed83a23029098dccd215). The authored wrapper supplies a known endpoint. Both adapters retain its source digest and /api/orders; MCP repeats successfully and exports full Evidence. The three-script capture is complete, with zero parse failures; the browser remains alive.
  • Completed CLI task: 9.765 s, family CPU 7.496 s, peak 876.9 MiB. MCP discovery/two captures/export/close: 32.748 s, CPU 30.323 s, peak 1,317.6 MiB; first/repeat calls 15.095 / 1.808 s. Reuse is uninstrumented and shared-host load uncontrolled; no speedup is claimed. Current worker tests use 2-GiB hard/1.5-GiB early bounds; baseline and public workflows use 8/7 GiB, all one CPU. Completed scopes verify cleanup and no OOM at payload exit. One initial run was stopped by the host-pressure guard and excluded.

cl100k_base estimates: compact input-catalog proxy 44,848, selected schema 229, response contents 1,350/1,350, complete captured protocol 722,603; local exported Evidence 1,322 tokens. These are protocol/local-data estimates, not model usage; initialization outside capture hooks and downstream prompts are unmeasured.

This verifies the original ordering at the producer boundary and healthy real-browser public workflows; it does not force that failure through a real browser. Original macOS, stdin EPIPE and actual OS/V8 exhaustion were not exercised. Raw captures and receipts remain locally under /tmp/rea-5419-proof. Closing as completed by the existing maintainer fix.

<!-- gh-comment-id:6296053674 --> @gensokyo-dev commented on GitHub (Oct 20, 2026): Independently verified as fixed on main `8716b5241125d81f1a16808eb5178fdfdea1ecdb` by maintainer commit [cac12ae33](https://github.com/morluto/rea/commit/cac12ae334e9e3ff0c6840f86620328b436e44f6). No additional production patch is needed. Linux x64 / Node 24.18.0 qualification: - Five selected real-owned-peer cases reproduce the report on pre-fix `861c11ec7`: failure frame, exit 7, malformed frame, oversized-stderr exit and resource failure are replaced by later cancellation. Current main passes **19 worker tests**; the separate deliberate heap-exhaustion test was excluded. Failure-first keeps typed diagnostics and opaque secondary abort; abort-first remains cancellation, including explicit null. Existing cleanup-retry and late-output completeness checks pass. - The public MCP timeout/Evidence/recovery boundary passes. Real Chrome **156.0.8078.4** public CLI and pinned MCP **2026-07-28** analyze three captured scripts, including unchanged **fast-deep-equal 3.1.3** (`index.js`, 1,177 bytes, SHA-256 `eb469e206280321a3878f2335ec98aa2104a155079d8ed83a23029098dccd215`). The authored wrapper supplies a known endpoint. Both adapters retain its source digest and `/api/orders`; MCP repeats successfully and exports full Evidence. The three-script capture is complete, with zero parse failures; the browser remains alive. - Completed CLI task: **9.765 s**, family CPU **7.496 s**, peak **876.9 MiB**. MCP discovery/two captures/export/close: **32.748 s**, CPU **30.323 s**, peak **1,317.6 MiB**; first/repeat calls **15.095 / 1.808 s**. Reuse is uninstrumented and shared-host load uncontrolled; no speedup is claimed. Current worker tests use 2-GiB hard/1.5-GiB early bounds; baseline and public workflows use 8/7 GiB, all one CPU. Completed scopes verify cleanup and no OOM at payload exit. One initial run was stopped by the host-pressure guard and excluded. `cl100k_base` estimates: compact input-catalog proxy 44,848, selected schema 229, response contents 1,350/1,350, complete captured protocol 722,603; local exported Evidence 1,322 tokens. These are protocol/local-data estimates, not model usage; initialization outside capture hooks and downstream prompts are unmeasured. This verifies the original ordering at the producer boundary and healthy real-browser public workflows; it does not force that failure through a real browser. Original macOS, stdin EPIPE and actual OS/V8 exhaustion were not exercised. Raw captures and receipts remain locally under `/tmp/rea-5419-proof`. Closing as completed by the existing maintainer fix.
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#3285
No description provided.