[GH-ISSUE #5294] [Bug] SQLite cleanup errors retain markers after root release #3227

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

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

Before submitting

  • Searched open and closed SQLite cleanup reports; #2612 concerns completed-reply Evidence and #5286 concerns final cancellation, not stale cleanup ownership.
  • Removed credentials and private paths.

Area

Process capture or runtime observation.

Observed behavior

A SQLite inspection error keeps cleanupIncomplete: true and the runtime-root path in cleanupResources after provider.close() successfully removes that root. A caller retaining the original error therefore sees a cleanup blocker for a resource the owner has released.

Reproduction

On Node 24.18.0/macOS arm64, run .cache/codebase-audit/sqlite-cleanup-marker-probe.mjs against the built SQLite provider. It creates an authored SQLite file, injects a worker-launch failure, denies root close on the first attempt, records the returned error, then allows provider.close() to remove the actual root. This reproduced once in the controlled probe. The saved output is .cache/codebase-audit/sqlite-cleanup-marker-probe.log.

Observed before retry: cleanupIncomplete: true, cleanupResources: [root path]. After successful close: rootExists: false, but the same returned error still has cleanupIncomplete: true and the same root path.

Expected behavior

Bind every returned inspection, retry, and close-aggregate cleanup error to the exact command and root owners it reports. Confirmed release should remove only that owner's resource markers and resolve the error only when all of its owners have released. A still-owned process or root must continue blocking replacement.

Impact and scope

The provider returns stale cleanup state after successful cleanup, which can cause callers and adapters to continue reporting a nonexistent root as unresolved. This was observed once with a controlled worker-launch failure and real temporary directory removal. Public CLI/MCP impact and frequency with ordinary inspections are NOT_ENOUGH_INFO.

REA version

Source revision e84e17e30 with unpublished local SQLite changes; package 6.7.0.

Runtime and platform

Node 24.18.0; macOS arm64.

Target and artifact context

An authored temporary SQLite fixture; the worker launcher intentionally throws. No external database or private artifact.

Interface

Other.

Earliest failure stage

Cleanup, cancellation, or lifecycle.

Relevant output or evidence

The probe log shows the exact returned error before and after successful provider.close(). The actual root no longer exists, while its cleanup marker remains. Source review confirms cleanupSqliteRoot creates a ProviderCleanupError, the pending map retains the cleanup work, and successful #cleanupPending deletes the root entry without resolving the returned error through an owner dependency.

Typed outcome or recovery expected

After root release, the original error should have cleanupIncomplete: false and cleanupResources: []. When a command owner and root are both pending, command release should remove only command resources, root release should remove the root path, and aggregate errors should track all unresolved owners.

Analysis or suspected cause

The returned cleanup error is not linked to the exact root/process cleanup owners. Retry success clears the provider's pending map but leaves the original AnalysisError cleanup state unchanged. The local source fix uses existing cleanup dependency binding and resolves owner markers only after process/root owners confirm release.

Local fix verification

The local provider now binds originating inspect errors, retry errors and close aggregates to distinct process and root markers, and descriptor close errors to their actual artifact leases. The completed-worker retry path reuses the existing cleanup-error builder, retaining partial Evidence across continued cleanup refusal. Controlled tests cover process release before denied root removal, multiple retained roots, blocked replacement admission, original/close error resolution and successful retry.

Executed on Node 24.18.0/npm 11.16.0, macOS 26.3 (Darwin 25.3.0) arm64: five SQLite suites plus public CLI/MCP acceptance controls passed 81 tests with 1 availability-gated skip. After the final retry-Evidence refactor, the three affected provider/cleanup/descriptor suites passed 45/45. Typecheck/lint, formatting and test discovery (1,675 files exactly once) passed. A rerun of the standalone probe now shows the original error with cleanupIncomplete: false, empty resources and the actual root absent after close.

These are local, uncommitted changes. Controlled process/root refusals do not establish natural OS failure frequency or native Linux/Windows behavior; CLI/MCP acceptance controls do not establish cancellation delivery through every client. No integrated fix or closure is claimed.

Primary-error recovery follow-up

A controlled follow-up found that successful owner cleanup cleared markers but left the returned error projecting as generic execution_failure. The root-cleanup builder and artifact-descriptor cleanup builder serialized the earlier failure into diagnostics without retaining its typed primary. The local fix now retains that primary at both owning boundaries.

An isolated snapshot of the current dirty pre-fix source, with only those two primary-retention additions removed, failed four controls after verified cleanup: a malformed worker reply projected execution_failure instead of unreadable_output after actual root deletion, and database/WAL/journal descriptor cancellations projected execution_failure instead of cancelled after actual descriptor/root release. This is a dirty-pre-fix control, not a clean upstream baseline. Fixed tests preserve the exact opaque cancellation cause without inheriting cleanup authority from it.

Final Node 24.18.0 validation on macOS 26.3 arm64: seven SQLite boundary suites passed 92 tests, 2 availability skips; snapshot limits passed 5/5. The coordinated current-source run passed 203 tests, 1 skip across 15 suites, including SQLite public CLI/MCP acceptance, MCP error recovery and shared process/native/EVM consumers. Typecheck/lint, formatting and discovery (1,688 test files exactly once) passed. Independent source review found no blocker and did not independently rerun tests. Fixes remain local and uncommitted; natural OS failure frequency and native Windows coverage remain unverified.

Originally created by @morluto on GitHub (Oct 19, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5294 ### Before submitting - [x] Searched open and closed SQLite cleanup reports; #2612 concerns completed-reply Evidence and #5286 concerns final cancellation, not stale cleanup ownership. - [x] Removed credentials and private paths. ### Area Process capture or runtime observation. ### Observed behavior A SQLite inspection error keeps `cleanupIncomplete: true` and the runtime-root path in `cleanupResources` after `provider.close()` successfully removes that root. A caller retaining the original error therefore sees a cleanup blocker for a resource the owner has released. ### Reproduction On Node 24.18.0/macOS arm64, run `.cache/codebase-audit/sqlite-cleanup-marker-probe.mjs` against the built SQLite provider. It creates an authored SQLite file, injects a worker-launch failure, denies root close on the first attempt, records the returned error, then allows `provider.close()` to remove the actual root. This reproduced once in the controlled probe. The saved output is `.cache/codebase-audit/sqlite-cleanup-marker-probe.log`. Observed before retry: `cleanupIncomplete: true`, `cleanupResources: [root path]`. After successful close: `rootExists: false`, but the same returned error still has `cleanupIncomplete: true` and the same root path. ### Expected behavior Bind every returned inspection, retry, and close-aggregate cleanup error to the exact command and root owners it reports. Confirmed release should remove only that owner's resource markers and resolve the error only when all of its owners have released. A still-owned process or root must continue blocking replacement. ### Impact and scope The provider returns stale cleanup state after successful cleanup, which can cause callers and adapters to continue reporting a nonexistent root as unresolved. This was observed once with a controlled worker-launch failure and real temporary directory removal. Public CLI/MCP impact and frequency with ordinary inspections are NOT_ENOUGH_INFO. ### REA version Source revision `e84e17e30` with unpublished local SQLite changes; package 6.7.0. ### Runtime and platform Node 24.18.0; macOS arm64. ### Target and artifact context An authored temporary SQLite fixture; the worker launcher intentionally throws. No external database or private artifact. ### Interface Other. ### Earliest failure stage Cleanup, cancellation, or lifecycle. ### Relevant output or evidence The probe log shows the exact returned error before and after successful `provider.close()`. The actual root no longer exists, while its cleanup marker remains. Source review confirms `cleanupSqliteRoot` creates a `ProviderCleanupError`, the pending map retains the cleanup work, and successful `#cleanupPending` deletes the root entry without resolving the returned error through an owner dependency. ### Typed outcome or recovery expected After root release, the original error should have `cleanupIncomplete: false` and `cleanupResources: []`. When a command owner and root are both pending, command release should remove only command resources, root release should remove the root path, and aggregate errors should track all unresolved owners. ### Analysis or suspected cause The returned cleanup error is not linked to the exact root/process cleanup owners. Retry success clears the provider's pending map but leaves the original `AnalysisError` cleanup state unchanged. The local source fix uses existing cleanup dependency binding and resolves owner markers only after process/root owners confirm release. ### Local fix verification The local provider now binds originating inspect errors, retry errors and close aggregates to distinct process and root markers, and descriptor close errors to their actual artifact leases. The completed-worker retry path reuses the existing cleanup-error builder, retaining partial Evidence across continued cleanup refusal. Controlled tests cover process release before denied root removal, multiple retained roots, blocked replacement admission, original/close error resolution and successful retry. Executed on Node 24.18.0/npm 11.16.0, macOS 26.3 (Darwin 25.3.0) arm64: five SQLite suites plus public CLI/MCP acceptance controls passed 81 tests with 1 availability-gated skip. After the final retry-Evidence refactor, the three affected provider/cleanup/descriptor suites passed 45/45. Typecheck/lint, formatting and test discovery (1,675 files exactly once) passed. A rerun of the standalone probe now shows the original error with `cleanupIncomplete: false`, empty resources and the actual root absent after close. These are local, uncommitted changes. Controlled process/root refusals do not establish natural OS failure frequency or native Linux/Windows behavior; CLI/MCP acceptance controls do not establish cancellation delivery through every client. No integrated fix or closure is claimed. ### Primary-error recovery follow-up A controlled follow-up found that successful owner cleanup cleared markers but left the returned error projecting as generic `execution_failure`. The root-cleanup builder and artifact-descriptor cleanup builder serialized the earlier failure into diagnostics without retaining its typed primary. The local fix now retains that primary at both owning boundaries. An isolated snapshot of the current dirty pre-fix source, with only those two primary-retention additions removed, failed four controls **after verified cleanup**: a malformed worker reply projected `execution_failure` instead of `unreadable_output` after actual root deletion, and database/WAL/journal descriptor cancellations projected `execution_failure` instead of `cancelled` after actual descriptor/root release. This is a dirty-pre-fix control, not a clean upstream baseline. Fixed tests preserve the exact opaque cancellation cause without inheriting cleanup authority from it. Final Node 24.18.0 validation on macOS 26.3 arm64: seven SQLite boundary suites passed **92 tests, 2 availability skips**; snapshot limits passed **5/5**. The coordinated current-source run passed **203 tests, 1 skip** across 15 suites, including SQLite public CLI/MCP acceptance, MCP error recovery and shared process/native/EVM consumers. Typecheck/lint, formatting and discovery (1,688 test files exactly once) passed. Independent source review found no blocker and did not independently rerun tests. Fixes remain local and uncommitted; natural OS failure frequency and native Windows coverage remain unverified.
Author
Owner

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

Cross-referencing with sibling SQLite provider lifecycle reports in Cluster AS: #5284 ([Bug] SQLite provider replaces owned worker failures with late cancellation) and #5286. All three target src/provider/sqlite/ where worker failures, root cleanup errors, and cancellation signals interact.

Consolidated under Cluster AS (SQLite Provider Cleanup, Worker Failure & Success-on-Cancellation) in tracker #4330.

<!-- gh-comment-id:6292127124 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with sibling SQLite provider lifecycle reports in **Cluster AS**: #5284 (`[Bug] SQLite provider replaces owned worker failures with late cancellation`) and #5286. All three target `src/provider/sqlite/` where worker failures, root cleanup errors, and cancellation signals interact. Consolidated under **Cluster AS (SQLite Provider Cleanup, Worker Failure & Success-on-Cancellation)** in tracker #4330.
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#3227
No description provided.