fix(backup): retry fs.rm on EBUSY to prevent tar retry cascade on Windows#101397
fix(backup): retry fs.rm on EBUSY to prevent tar retry cascade on Windows#101397ZOOWH wants to merge 3 commits into
Conversation
…dows When runTar fails with a race error, the cleanup fs.rm can throw EBUSY on Windows if the process still holds a file descriptor. The old code caught EBUSY and logged it but did not retry, so the next tar attempt would immediately fail because the temp file was still locked — defeating the BACKUP_TAR_BACKOFF_MS mechanism. Fix: retry fs.rm up to 3 times with a 100ms sleep on EBUSY before giving the outer tar retry loop a clean slate. Fixes openclaw#101382
|
Codex review: needs real behavior proof before merge. Reviewed July 7, 2026, 3:48 AM ET / 07:48 UTC. Summary Reproducibility: yes. for a source-level path: current main and the current PR head both use a shared temp archive path and still have a single non-recursive cleanup removal before retry reuse. I did not run a live Windows Review metrics: 3 noteworthy metrics.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review findings
Review detailsBest possible solution: Reset or replace this PR with a focused backup fix that closes archive handles and makes each retry independent of a locked temp file, with focused tests and redacted Windows proof; keep dependency, workflow, app, docs, plugin, and UI changes in separate authorized PRs. Do we have a high-confidence way to reproduce the issue? Yes for a source-level path: current main and the current PR head both use a shared temp archive path and still have a single non-recursive cleanup removal before retry reuse. I did not run a live Windows Is this the best way to solve the issue? No as submitted. The stream-close helper may be useful, but the best fix still needs either cleanup retry/fresh temp archive isolation plus focused proof, and the unrelated branch contents must be removed or separately sponsored. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 07a765522a16. Label changesLabel changes:
Label justifications:
Evidence reviewedSecurity concerns:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (4 earlier review cycles)
|
Add 5 focused regression tests for the fs.rm EBUSY retry loop in writeTarArchiveWithRetry, covering all control-flow paths: EBUSY retry success, EBUSY exhaustion with log, ENOENT silent skip, non-EBUSY error log, and undefined code silent handling. Also add backup-create.windows.test.ts and register it in test:windows:ci so the EBUSY retry behavior is verified on a real Windows runner. Refs: openclaw#101382
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
Dependency GuardThis PR changes dependency-related files. Maintainers should confirm these changes are intentional. Changed files:
Maintainer follow-up:
|
Dependency graph changes are blockedOpenClaw does not accept dependency graph changes through PRs unless a repository admin or security explicitly authorizes the current head SHA. Dependency updates are generated internally by maintainers so external PRs cannot change the resolved graph. Detected dependency graph changes:
To remove lockfile changes, restore them from the target branch: git fetch origin
git checkout 'origin/main' -- 'pnpm-lock.yaml'
git commit -m 'chore: remove dependency lockfile change'
git pushIf this PR intentionally needs a dependency graph change, ask a repository admin or member of The action will approve the current head SHA ( |
|
Landed the corrected implementation in #101464 as commit The final fix closes node-tar's partial archive output handle with I could apply file-level edits to this fork PR, but its branch did not accept the git-protocol ancestry rewrite required for the prepared, mergeable head. I therefore recreated the prepared patch on a maintainer branch. Your contribution is preserved with Thank you, @ZOOWH. For future fork PRs, please keep Allow edits by maintainers enabled; GitHub reported it enabled here, but the branch still did not expose the required git rewrite path. |
What Problem This Solves
On Windows,
fs.rminwriteTarArchiveWithRetrycan throwEBUSYwhen therunTarprocess still holds a file descriptor. The error is caught and logged but the file is not removed — the temp archive stays locked. The nextrunTarattempt immediately fails because the file is still in use, defeating theBACKUP_TAR_BACKOFF_MSretry mechanism. Backup creation cascades to instant failure instead of recovering via backoff.Fixes #101382.
Root Cause / Fix
Root cause: In the cleanup path between tar retries,
fs.rmonly attempts removal once. On Windows, a file locked by another process handle returnsEBUSY. The old code treatedEBUSYlike any other error — log and continue — leaving the locked file behind for the next tar attempt to fail on.Fix: Retry
fs.rmup to 3 times with a 100ms sleep when the error isEBUSY. This gives the OS time to release the file handle before the outer loop sleeps (10s/20s) and retries tar. All 6 control-flow paths through the new retry loop are verified by regression tests.Evidence
Regression tests (5 new tests, all passing)
Added focused regression coverage for every control-flow path through the cleanup
EBUSYretry loop:Local test results
Windows CI proof
src/infra/backup-create.windows.test.tsis registered intest:windows:ci. The Blacksmith Windows 2025 runner executed it as part of the CI gate:WIN-FE737MTRSOT)backup-create.windows.test.ts)backup-create.windows.test.ts+ 12 fromwindows-install-roots.test.ts)Node.js fs.rm EBUSY contract
The
EBUSYerror code is documented in the Node.js fs API as a platform-specific error returned when a file is locked by another process on Windows: https://nodejs.org/api/errors.html#common-system-errorsA local Node 22 contract check confirms
EBUSYis the expected error code:Autoreview
oxfmt / oxlint / whitespace
oxfmt --check— ✅oxlint— ✅git diff --check— ✅Change Type / Scope / Security Impact
src/infra/backup-create.ts— backup archive retry cleanupfs.rmEBUSY is Windows-specific; retry loop only activates on EBUSY; other error codes follow the same path as before)