Repository navigation
fix(server): Windows saves no longer fail while a scanner holds the file - #16574
yaacovcorcos wants to merge 1 commit into
Conversation
On Windows, replacing a file fails with EPERM, EACCES, or EBUSY while another process such as a virus scanner or the search indexer briefly has it open. writeFileStringAtomically renamed the temp file over the target once, so settings, keybindings, and cache writes failed outright. Retry the rename on those codes on Windows with a short capped backoff (about 3 seconds in total). Other errors and other platforms still fail at once, and the target keeps its old contents until a rename succeeds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📝 WalkthroughWalkthroughAtomic file writes now retry selected rename failures on Windows. The retry policy uses exponential delays and a limit of 10 retries. Tests cover transient failures, persistent failures, and errors that do not trigger retries. ChangesAtomic file writes
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to The Windows retry path matches the filesystem provider’s error shape. No issue identified here prevents merging after normal checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change is bounded to selected Windows errors and does not add an entrypoint or increase filesystem privileges. However, a delayed retry can overwrite a newer settings update from another process, potentially restoring an older security-relevant configuration. This requires concurrent writers with existing access to the same file. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a small, localized Windows reliability fix: existing atomic saves retry only transient rename-lock errors, while other platforms and failures remain unchanged. The added tests cover success, exhaustion, and retry boundaries without affecting production behavior. You can add or adjust custom eligibility rules. Learn more. |
Problem
On Windows, renaming over a file fails with
EPERM,EACCES, orEBUSYwhile another process, such as a virus scanner or the search indexer, briefly has the target open.writeFileStringAtomically(apps/server/src/atomicWrite.ts) renames its temp file over the target once, so a save that lands in that window fails outright. Every caller is affected: server settings, keybindings, provider status and model caches, runtime state, and the other files written through this helper.Fix
Retry the final rename on Windows when it fails with one of those three codes, with a short exponential backoff capped at 500 ms per wait and 10 retries (about 3 seconds in total). Every other error, and every error on other platforms, still fails on the first attempt. The temp file stays in its private temp directory until a rename succeeds, so the target never shows partial contents, and a lock that does not clear fails the save with the old contents intact.
Why it qualifies
Very small, focused fix for an obvious bug: one rename in one helper, no behaviour change off Windows or for other errors.
How it was checked
New tests in
apps/server/src/atomicWrite.test.ts. They wrap the real file system and replacerename, and setHostProcessPlatformtowin32. Retry waits run onTestClock; nothing sleeps.retries a Windows replacement while another process holds the target:renamefails twice withEBUSY, then the real rename runs. The save must succeed, the file must have the new contents, and no temp entry may be left.gives up on a Windows lock that does not clear and keeps the old contents:renamealways fails withEPERM. The save must fail after more than one attempt, with the old contents and no temp entry left.fails a rename at once outside Windows or for other errors:EPERMon Linux andEXDEVon Windows are each tried exactly once. This guards the scope of the retry and passes before and after.Before the fix (
apps/server/src/atomicWrite.tsreverted, tests kept), the first two fail:After the fix:
Other checks:
cd apps/server && ../../node_modules/.bin/vp run typecheck: no new errors. The same 10 errors insrc/process/externalLauncher.test.tsalso appear on unmodified main9bd1d8009a.vp lint --report-unused-disable-directives apps/server/src: exit 0, no findings in the touched files.vp fmt --check apps/server/src: clean.Not checked
This was not run on real Windows. The tests simulate the lock by failing
renamewith the Node error codes Windows reports. They do not prove which codes a given scanner produces, or that about 3 seconds is always long enough.Found and fixed in a downstream fork (Scient), then ported to current main. Ported and verified with Claude Opus 5.5 in Claude Code.
🤖 Generated with Claude Code