Fix: npm ERR! ENOTEMPTY: directory not empty, rename
Part of: JavaScript & TypeScript Errors
Quick Answer
How to fix npm ENOTEMPTY directory not empty error caused by corrupted cache, concurrent npm operations, stale node_modules, and Windows file locking issues.
The Error
You run npm install and get:
npm ERR! code ENOTEMPTY
npm ERR! syscall rename
npm ERR! path /home/user/project/node_modules/some-package
npm ERR! dest /home/user/project/node_modules/.some-package-xyz
npm ERR! errno -39
npm ERR! ENOTEMPTY: directory not empty, rename '/home/user/project/node_modules/some-package' -> '/home/user/project/node_modules/.some-package-xyz'Or on Windows:
npm ERR! code EPERM
npm ERR! syscall rename
npm ERR! path C:\Users\user\project\node_modules\some-package
npm ERR! errno -4048
npm ERR! EPERM: operation not permitted, rename 'C:\Users\user\project\node_modules\some-package'npm tried to move a directory during installation and failed because the target directory already has files in it, or the operating system blocked the operation.
Why This Happens
During npm install, npm moves packages around in node_modules — renaming directories as part of its installation algorithm. The rename is the atomic commit step of the install: npm writes the new package into a hidden staging directory (.package-xyz), then renames it over the final location. If the final location is not empty (because a partial previous install left files there, or another process wrote into it) the rename fails with ENOTEMPTY. If the OS refuses the rename (because a file inside the source or destination is locked) it fails with EPERM.
Two-step rename installs were introduced in npm 7’s arborist engine specifically to make installs more atomic. The trade-off is that any concurrent process touching node_modules mid-install can break the rename. That includes a second npm install, a file watcher, an antivirus scanner, a Docker volume mount with a slow sync layer, or a CI cache restore that races with the install step.
The rename operation fails when:
- Corrupted npm cache. The local cache has stale or corrupted entries that conflict with the installation.
- Concurrent npm processes. Two npm commands running at the same time modify the same
node_modulesdirectory. - Stale
node_modules. A previous interrupted installation leftnode_modulesin an inconsistent state. - File locking on Windows. An editor, file watcher, or antivirus is holding a lock on files inside
node_modules. - Filesystem race condition. On network drives (NFS, SMB) or Docker volumes, file operations may not be atomic.
- npm bug. Some npm versions have known issues with the rename operation on specific platforms.
In Production: Incident Lens
In production CI/CD, ENOTEMPTY is the classic intermittent deploy failure. Most builds pass. Then one Tuesday at 3pm three deploys fail in a row, then it works again, then it fails the next morning. The error is the same each time but the package it cites is different — first react, then lodash, then next. Engineers retry the job, it passes, and the on-call shrugs.
The real culprit is almost always shared CI cache between concurrent jobs. Two PRs merge close together. Both CI pipelines start, both restore the same shared node_modules cache, both try to write into the cached directory. One of them wins the rename and the other gets ENOTEMPTY for whichever package it tries to write second. Or your CI runner reuses a workspace between jobs without cleaning node_modules, and a previous job’s partial install poisons the next one.
The blast radius is per-deploy. The product is unaffected; the cost is engineering time chasing flaky CI plus the deploy throughput hit when retries pile up. But that cost compounds: if 5% of deploys flake, every release train slows down, and incident response slows down because hotfix deploys are no longer reliable.
The monitoring signal to set up is build-failure rate per pipeline, grouped by error class. Pipe CI exit logs through a log aggregator and create a saved query for ENOTEMPTY|EPERM.*rename. Alert when the rolling 24-hour count crosses a threshold — three failures in a day, or any failure on a release branch. Many teams discover months of accumulated ENOTEMPTY noise the moment they start measuring it.
Recovery sequence (per-incident): kill the failing job, clear that runner’s node_modules and npm cache, retry. If the failure repeats on a fresh runner, the lockfile is in a bad state — delete package-lock.json, run npm install locally, commit the new lockfile, retry CI. If it repeats across runners, your cache key is too broad and is poisoning every runner; bump the cache key and force a fresh install.
Postmortem preventive: make the cache key per-OS, per-Node-version, and per-package-lock.json-hash so concurrent jobs on different runners never share a partial cache. Always use npm ci (not npm install) in CI — npm ci deletes node_modules before installing, eliminating the partial-state class of failures. Mark the npm cache as immutable in your cache action (restore-keys only, no save on PR builds). Pair it with a CI workspace cleanup step at job start that removes node_modules and node_modules/.cache before anything else runs.
Fix 1: Delete node_modules and Reinstall
The most reliable fix. Remove the corrupted state and start fresh:
rm -rf node_modules
npm installOn Windows (if rm is not available):
rmdir /s /q node_modules
npm installIf rm -rf itself fails with permission errors, close all editors and terminals that might have files open in node_modules, then try again.
Pro Tip: If you frequently need to delete
node_modules, use thenpx npkillcommand to interactively find and removenode_modulesdirectories across your system. It is faster than manual deletion and shows how much disk space each one uses.
Fix 2: Clear the npm Cache
A corrupted cache causes npm to create conflicting files during installation:
npm cache clean --forceThen reinstall:
rm -rf node_modules
npm installVerify the cache is intact:
npm cache verifyThis checks the cache for consistency and removes corrupted entries.
The --force flag is required because npm warns against cleaning the cache (it is usually self-healing). In this case, cleaning is necessary.
Fix 3: Delete package-lock.json and Reinstall
If the lock file is inconsistent with the current node_modules state:
rm -rf node_modules package-lock.json
npm installThis regenerates both node_modules and package-lock.json from scratch. Warning: This might change your dependency versions if package.json uses ranges (^, ~). If you need exact reproducibility, only delete node_modules and keep package-lock.json.
If the lock file has dependency resolution conflicts rather than rename errors, see Fix: npm ERESOLVE unable to resolve dependency tree.
Fix 4: Fix Windows File Locking
On Windows, EPERM or ENOTEMPTY errors are often caused by other processes locking files in node_modules:
Close these before running npm install:
- VS Code (or any editor with file watchers)
- Terminal windows with
cdinside the project - File Explorer windows showing
node_modules - Windows Defender real-time scanning
- Docker Desktop file sharing
Disable Windows Defender for your project directory:
- Open Windows Security → Virus & threat protection
- Click “Manage settings” under Virus & threat protection settings
- Scroll to Exclusions → Add an exclusion
- Add your project directory
Use PowerShell as administrator:
Remove-Item -Recurse -Force node_modules
npm installCommon Mistake: Running
npm installwhile VS Code is open with the project. VS Code’s file watcher monitorsnode_modulesfor changes, which can lock files during npm’s rename operations. Close VS Code or disable the file watcher fornode_modulesby adding it to thefiles.watcherExcludesetting.
Fix 5: Fix Concurrent npm Operations
If two terminal tabs are running npm commands on the same project:
# Check for running npm processes
ps aux | grep npm
# On Windows
tasklist | findstr nodeKill any stale npm processes:
# Linux/macOS
kill <pid>
# Windows
taskkill /PID <pid> /FFix: Remove the npm lock file:
npm creates a .package-lock.json in node_modules to prevent concurrent operations. If a previous npm process crashed, this lock might be stale:
rm -rf node_modules/.package-lock.json
npm installFix 6: Fix Docker and Container Environments
When running npm install inside a Docker container with a mounted volume, the host filesystem may not support atomic renames:
Fix: Copy files instead of mounting node_modules:
# In Dockerfile
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .Do not mount node_modules from the host. Use a named volume or exclude it:
# docker-compose.yml
services:
app:
volumes:
- .:/app
- /app/node_modules # Anonymous volume — npm installs inside containerThe anonymous volume for node_modules means the container’s npm-installed packages are not mapped to the host filesystem, avoiding cross-platform rename issues.
If npm install fails with a different error inside Docker, check if the issue is related to npm ERR! cb() never called or network connectivity.
Fix 7: Use npm ci Instead of npm install
npm ci (clean install) is designed for CI/CD and avoids many rename issues:
npm cinpm ci differences from npm install:
- Deletes
node_modulesentirely before installing - Installs exact versions from
package-lock.json - Does not modify
package-lock.json - Faster because it skips the resolution step
This is often the fix for ENOTEMPTY errors because it starts with a clean node_modules every time.
Fix 8: Update npm
Some versions of npm have bugs that cause ENOTEMPTY errors. Update to the latest:
npm install -g npm@latestIf updating npm itself fails with permission errors, see Fix: npm EACCES permission denied global install.
Check your current version:
npm --versionKnown problematic versions: npm v6 had several ENOTEMPTY bugs on Windows that were fixed in npm v7+. If you are on npm v6, upgrading to v9 or v10 resolves most of these issues.
Fix 9: Fix NFS and Network Drive Issues
On network filesystems (NFS, CIFS/SMB, shared Docker volumes), file operations may not be atomic:
Symptoms:
ENOTEMPTYerrors are intermittent- The error happens on different packages each time
- The same project installs fine on a local filesystem
Fix: Install on a local filesystem:
Move the project to a local drive:
cp -r /mnt/nfs/project ~/local-project
cd ~/local-project
npm installFix: Use --prefer-offline to reduce network operations:
npm install --prefer-offlineFix: Increase the npm retry settings:
npm config set fetch-retries 5
npm config set fetch-retry-mintimeout 20000
npm installStill Not Working?
If the error persists after trying all fixes:
Try yarn or pnpm. Alternative package managers handle node_modules differently and may not have the same rename issues:
# Using yarn
npx yarn install
# Using pnpm
npx pnpm installpnpm uses a content-addressable store and hard links instead of copying packages, which avoids most rename and disk space issues.
Check filesystem permissions. The user running npm must have write permissions to the project directory:
ls -la node_modules/If owned by root, fix permissions:
sudo chown -R $(whoami) node_modules/Check disk space. Running out of disk space during install can cause partial renames that leave directories in an inconsistent state:
df -h .Use the --verbose flag for more information:
npm install --verboseThis shows detailed logs of every file operation, which can help identify exactly which package and file causes the error.
Check for antivirus interference. Some antivirus software (McAfee, Norton, Kaspersky) lock files immediately after creation for scanning. This interferes with npm’s rapid create-rename cycle. Temporarily disable real-time scanning or exclude the project directory.
Check your CI cache key. A cache key like npm-cache shared across every job, OS, and Node version guarantees ENOTEMPTY collisions during concurrent runs. Use npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }} (GitHub Actions syntax) so each lockfile and OS pair has its own cache namespace. If you cannot change the cache key today, set the workflow’s concurrency group to serialize jobs on the same branch.
Check for a leftover .staging directory inside node_modules. Older npm versions used node_modules/.staging and could leave it behind after a kill -9. rm -rf node_modules/.staging plus a retry often clears a stubborn ENOTEMPTY that no cache clean fixes.
Check for case-sensitivity drift between dev and CI. macOS and Windows filesystems are case-insensitive by default; Linux CI runners are case-sensitive. A lockfile that resolved React on a Mac can produce react and React as separate directories on Linux, and the rename of one over the other fails. Run git config core.ignorecase false locally and re-resolve the lockfile to catch this before it reaches CI.
For missing-package errors, see Fix: npm ENOENT no such file.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: pnpm Peer Dependency Errors (Missing or Incompatible Peer Dependencies)
How to fix pnpm peer dependency errors — why pnpm is stricter than npm about peer deps, how to resolve missing peers, configure peerDependencyRules, and handle incompatible version ranges.
Fix: npm ERR! enoent ENOENT: no such file or directory
How to fix the npm ENOENT no such file or directory error caused by missing package.json, wrong directory, corrupted node_modules, broken symlinks, and npm cache issues.
Fix: npm WARN deprecated — inflight, glob, rimraf, and Other Package Warnings
npm WARN deprecated [email protected], [email protected], [email protected], @humanwhocodes — which warnings are safe to ignore, which need fixing, and exact steps to silence each one.
Fix: npm ERR! cb() never called
Resolve npm's cb() never called error by clearing cache, fixing network issues, updating npm, and resolving corrupted package-lock.json files.