npm "ENOTEMPTY: directory not empty, rename" - Fix Stale node_modules in CI
npm installs packages by staging them and renaming into place. ENOTEMPTY on that rename means the destination directory already had unexpected contents - a leftover from a stale node_modules, a restored partial cache, or a concurrent process.
What this error means
npm install/npm ci fails with ENOTEMPTY: directory not empty, rename ... pointing at a .staging or package path under node_modules. It often follows a restored cache or an interrupted previous install.
npm error code ENOTEMPTY
npm error syscall rename
npm error path /app/node_modules/.staging/lodash-XXXX
npm error dest /app/node_modules/lodash
npm error errno -39
npm error Error: ENOTEMPTY: directory not empty, rename '...' -> '...'Diagnose it: what is different about the runner?
A build that passes locally and fails on a runner differs in a small number of predictable ways. Check those before changing build configuration, because the build config is usually not the thing that changed.
- run: |
node --version && npm --version
echo "NODE_ENV=$NODE_ENV CI=$CI"
nproc && free -h && df -h /
ls -la node_modules/.bin | headCommon causes
A stale or partially populated node_modules
Leftover package directories from an interrupted install (or a restored partial cache) make the destination non-empty when npm tries to rename the staged copy in.
A concurrent process touching node_modules
A watcher, a parallel install, or antivirus holding files under node_modules can leave a directory non-empty at the moment of rename.
How to fix it
Start from a clean node_modules
Remove the existing tree and reinstall so the rename targets are empty.
rm -rf node_modules
npm cache verify
npm ciRemove contention
- Ensure no other step, watcher, or process touches node_modules during install.
- Do not cache a partial node_modules across runs.
- Run installs sequentially, not in parallel, for the same project.
The three that account for most of them
- Case sensitivity. Linux runners are case sensitive, macOS is not. An import with the wrong case resolves locally and fails in CI.
- Out of memory. Exit code 137 is a SIGKILL from the kernel, not a build error. Raise
--max-old-space-sizeor use a larger runner. - devDependencies pruned.
NODE_ENV=productionmakesnpm ciskip devDependencies, so the build tool itself goes missing. Set it after install, not before.
How to prevent it
- Use
npm ciso node_modules starts empty. - Never cache or restore a partial node_modules.
- Avoid concurrent processes touching the module tree.