Skip to content

fix: use graceful-fs to retry transient EPERM/EACCES/EBUSY on Windows rename#228

Closed
manzoorwanijk wants to merge 1 commit intonpm:mainfrom
manzoorwanijk:fix/use-graceful-fs-for-windows-rename-retry
Closed

fix: use graceful-fs to retry transient EPERM/EACCES/EBUSY on Windows rename#228
manzoorwanijk wants to merge 1 commit intonpm:mainfrom
manzoorwanijk:fix/use-graceful-fs-for-windows-rename-retry

Conversation

@manzoorwanijk
Copy link
Copy Markdown

Summary

On Windows, fs.rename fails with EPERM: operation not permitted when another process (Windows Defender, Windows Search indexer, or a concurrent Node.js worker) holds a transient lock on the target file. This is especially prominent with npm's install-strategy=linked, where many files are written in parallel under a single .store/ directory, increasing the window for antivirus lock conflicts.

The root cause is that write-file-atomic uses bare require('fs'), which has no retry logic. graceful-fs already patches fs.rename on Windows with exponential backoff (up to 60s) for EACCES/EPERM/EBUSY errors (polyfills.js#L96-L120).

This PR switches from require('fs') to require('graceful-fs') to get that retry behavior.

Background

graceful-fs was originally a dependency of write-file-atomic but was removed in v3.0.0 to break a circular dependency that interfered with graceful-fs's own test suite (isaacs/node-graceful-fs#163). That circular dependency no longer exists since tap no longer depends on write-file-atomic.

Changes

  • lib/index.js: Changed require('fs') to require('graceful-fs')
  • package.json: Added graceful-fs (^4.2.11) as a dependency
  • test/basic.js, test/concurrency.js, test/integration.js: Updated t.mock() calls to mock 'graceful-fs' instead of 'fs'

Related issues

References

Fixes #227, #28

graceful-fs patches fs.rename on Windows with exponential backoff for
EACCES/EPERM/EBUSY, fixing failures caused by antivirus or indexer
file locks during concurrent writes.

Fixes npm#227
@manzoorwanijk manzoorwanijk force-pushed the fix/use-graceful-fs-for-windows-rename-retry branch from 892db2d to d150f7c Compare February 28, 2026 12:36
@manzoorwanijk
Copy link
Copy Markdown
Author

@owlstronaut any thoughts on this to ultimately fix npm/cli#9021?

@wraithgar
Copy link
Copy Markdown
Member

npm already fully patches require('node:fs') with graceful-fs before it does ANYTHING else: https://github.com/npm/cli/blob/10d530242c7d893c562456013bb1c5104ca3e3b8/lib/cli/entry.js#L8

This also patches require('fs'), and will patch anything that has already required either of them. Both return the same singleton.

This was specifically to not have to worry about this in our subdependencies (and avoid problems like the one that cause us to remove it in 3.0.0). If things are not working as expected now then this PR shouldn't help either.

@manzoorwanijk
Copy link
Copy Markdown
Author

manzoorwanijk commented Mar 4, 2026

npm already fully patches require('node:fs') with graceful-fs before it does ANYTHING else

So, after the latest changes in npm/cli#9028, we no longer need this specifically for npm, right?

@wraithgar
Copy link
Copy Markdown
Member

So, after the latest changes in npm/cli#9028, we no longer need this specifically for npm, right?

Yeah, reading your latest comment in there I think so. This PR doesn't need to exist.

@wraithgar wraithgar closed this Mar 4, 2026
@manzoorwanijk manzoorwanijk deleted the fix/use-graceful-fs-for-windows-rename-retry branch March 4, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] EPERM on Windows: fs.rename fails due to transient file locks (no retry)

2 participants