Error: EPERM: operation not permitted most often on Windows when npm install tries to rename or delete a file inside node_modules that another process (VS Code, antivirus, a stuck node.exe) holds open. Fix: (1) close VS Code; (2) kill all node.exe processes in Task Manager; (3) add node_modules to Windows Defender exclusions; (4) delete node_modules with npx rimraf node_modules; (5) run npm cache clean --force; (6) rerun npm install. For symlink errors, enable Windows Developer Mode.
What is EPERM?
EPERM stands for Error: operation not PERMitted — a
POSIX system error
(errno 1 on Linux/macOS, errno -4048 on Windows) returned by the OS kernel when a
file system operation is refused at a level above ordinary file permission bits.
It is important to distinguish EPERM from its sibling
EACCES (permission denied): EACCES means the process lacks read/write/execute
permission on a specific file or directory (a chmod/chown problem).
EPERM means the operation itself is not permitted in the current OS context, regardless
of file permissions — for example, creating a symlink without the required Windows privilege,
renaming a file another process has locked, or writing to a read-only filesystem.
Error: EPERM: operation not permitted, rename 'C:\project\node_modules\.staging\...' -> 'C:\project\node_modules\...'Error: EPERM: operation not permitted, unlink 'C:\project\node_modules\some-package\index.js'Error: EPERM: operation not permitted, symlink '../some-bin' -> 'C:\project\node_modules\.bin\some-bin'Error: EPERM: operation not permitted, mkdir 'C:\project\node_modules\.cache'npm ERR! code EPERMnpm ERR! syscall renamenpm ERR! path C:\Users\user\AppData\Roaming\npm-cache\_cacache\tmp\...
Full Error Example
npm ERR! code EPERM
npm ERR! syscall rename
npm ERR! path C:\project\node_modules\.staging\lodash-9a4f2c1b\package.json
npm ERR! dest C:\project\node_modules\lodash\package.json
npm ERR! errno -4048
npm ERR! Error: EPERM: operation not permitted, rename
npm ERR! 'C:\project\node_modules\.staging\lodash-9a4f2c1b\package.json'
npm ERR! -> 'C:\project\node_modules\lodash\package.json'
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR! If you believe this might be a permissions problem, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.
at Object.rename (node:fs:1281:3) {
errno: -4048,
code: 'EPERM',
syscall: 'rename',
path: 'C:\\project\\node_modules\\.staging\\lodash-9a4f2c1b\\package.json',
dest: 'C:\\project\\node_modules\\lodash\\package.json'
}
Key diagnostic fields on the error object:
| Field | Value in example | Meaning |
|---|---|---|
code | 'EPERM' | Operation not permitted — OS-level refusal |
syscall | 'rename' | The fs operation that failed: rename, unlink, symlink, mkdir, open, rmdir |
path | staging path | The source path — check which process owns this file |
dest | final path | The destination (present on rename errors) |
errno | -4048 | Windows error mapping; -1 on Linux/macOS |
EPERM vs. EACCES — Know Which You Have
| Error | Kernel meaning | Common Node.js scenario | Primary fix |
|---|---|---|---|
EPERM |
Operation not permitted in this context | Symlink without privilege, rename of locked file, write to read-only FS | Close conflicting process, enable Developer Mode, run as admin |
EACCES |
Permission bits deny access | chmod 000 file then read it; npm global install without sudo |
chmod +r / chown / fix npm prefix |
On Windows, EPERM is used far more broadly than POSIX strictly defines it because the Windows
kernel maps many error conditions (sharing violations, privilege checks) to a single EPERM code.
If you see EPERM during npm install on Windows, a file handle conflict
is the first thing to investigate, not file permission bits.
All Causes at a Glance
| Cause | Syscall in error | OS most affected | Fix summary |
|---|---|---|---|
IDE/file watcher holding a handle in node_modules | rename, unlink | Windows | Close VS Code; kill all node.exe processes |
Antivirus scanning node_modules while npm writes | rename, mkdir | Windows | Add node_modules and npm cache to exclusions |
Stuck node.exe or npm.cmd process from prior run | rename, rmdir | Windows | Task Manager → end node.exe / npm.cmd |
Symlink creation without SeCreateSymbolicLinkPrivilege | symlink | Windows | Enable Developer Mode or run as Administrator |
| Corrupted npm cache | rename | All (worse on Windows) | npm cache clean --force |
| Read-only file or directory | open, unlink, rename | All | chmod +w (Linux/macOS) / attrib -r (Windows) |
Project on NTFS mount in WSL (/mnt/c/...) | symlink, rename | WSL | Move project to WSL native filesystem (~/projects/) |
| Mixing Windows Node.js and WSL Node.js on same path | varies | WSL | Use Linux Node.js exclusively within WSL shell |
| npm cache/prefix on a network share | rename | Windows (corporate) | Move npm cache to local path with npm config set cache |
| Long paths exceeding 260 characters | open, mkdir | Windows | Enable Win32 long paths; use rimraf |
Writing to C:\Program Files or system paths | open, mkdir | Windows | Move project to user home directory |
Cause 1 – IDE / File Watcher Holds an Open Handle
This is by far the most common cause on Windows. Visual Studio Code, WebStorm, and other
IDEs use file system watchers to provide features like auto-import, IntelliSense, and live
reload. These watchers hold open read handles on files inside node_modules. When
npm install needs to rename a staging file into its final location — or delete an
old version of a package — Windows refuses the operation with EPERM because the file is held
by the IDE.
# Symptom — seen during npm install or npm ci
npm ERR! code EPERM
npm ERR! syscall rename
npm ERR! path C:\project\node_modules\.staging\react-abc123\index.js
npm ERR! dest C:\project\node_modules\react\index.js
Fix: close VS Code and kill node processes
# PowerShell — kill all Node.js and npm processes
Get-Process node, npm -ErrorAction SilentlyContinue | Stop-Process -Force
# Then verify no node processes remain
Get-Process node -ErrorAction SilentlyContinue
After killing the processes, wait 5–10 seconds for Windows to release all file handles, then
rerun npm install. If the error still occurs, reboot — Windows releases all
handles on reboot unconditionally.
node_modules to
files.watcherExclude so the editor never watches inside it:
// .vscode/settings.json
{
"files.watcherExclude": {
"**/node_modules/**": true
},
"files.exclude": {
"**/node_modules": true
}
}
Cause 2 – Antivirus Scanning node_modules
Windows Defender and third-party antivirus software (McAfee, Norton, Avast, Kaspersky) scan
each file as it is written to disk. When npm install unpacks hundreds of small
files rapidly, the antivirus scanner opens each file for reading at the exact moment npm tries
to rename it from the staging directory. The rename fails with EPERM.
This cause produces intermittent EPERM failures — the error appears on some runs but not others, depending on which file the antivirus happened to be scanning at that moment.
Fix: add exclusions to Windows Defender
# PowerShell — add project node_modules to Defender exclusions
Add-MpPreference -ExclusionPath "C:\project\node_modules"
# Also exclude the npm cache directory
Add-MpPreference -ExclusionPath "$env:APPDATA\npm-cache"
# Or exclude by process: exclude node.exe and npm.cmd from scanning
Add-MpPreference -ExclusionProcess "node.exe"
Add-MpPreference -ExclusionProcess "npm.cmd"
Via GUI: Windows Security → Virus & threat protection → Manage settings → Exclusions → Add or remove exclusions → Add an exclusion → Folder → select your project root.
node_modules from antivirus scanning is
standard practice in professional development environments — npm's own documentation recommends
it. The packages installed via npm are from a registry with its own integrity checks
(package-lock.json SHA hashes). However, never exclude paths you do not control.
Cause 3 – Stuck node.exe or npm.cmd Processes
A prior npm install that was Ctrl+C'd, a crashed dev server, or a runaway
build script can leave a node.exe process running in the background with files
in node_modules still open. The next npm install then fails because
Windows will not rename or delete an open file.
# Command Prompt — find all node and npm processes
tasklist | findstr /i "node npm"
# Kill by name (closes all instances)
taskkill /IM node.exe /F
taskkill /IM npm.cmd /F
# PowerShell alternative
Get-Process -Name node, npm -ErrorAction SilentlyContinue | Stop-Process -Force
# Verify they are gone
Get-Process -Name node -ErrorAction SilentlyContinue
Cause 4 – Symlink Creation Without Sufficient Privilege
npm creates symbolic links in node_modules/.bin/ for every package that exposes
a CLI binary. On Windows, creating a symlink (as opposed to a junction) requires the
SeCreateSymbolicLinkPrivilege. Standard user accounts do not have this privilege
by default — only Administrator accounts and accounts with Developer Mode enabled.
npm ERR! code EPERM
npm ERR! syscall symlink
npm ERR! path ..\typescript\bin\tsc
npm ERR! dest C:\project\node_modules\.bin\tsc
npm ERR! errno -4048
npm ERR! Error: EPERM: operation not permitted, symlink
npm ERR! '..\typescript\bin\tsc' -> 'C:\project\node_modules\.bin\tsc'
Fix A – Enable Windows Developer Mode (recommended)
# Settings → System → For developers → Developer Mode → On
# (No UAC prompt required, persists across reboots)
# After enabling, run npm install normally — no Administrator terminal needed
Fix B – Run terminal as Administrator
# Right-click "Command Prompt" or "PowerShell" → "Run as administrator"
# Then run: npm install
Fix C – Use --no-bin-links (workaround, not a full fix)
# Skips creating .bin symlinks entirely
# CLI tools installed via npm will NOT be directly runnable as commands
npm install --no-bin-links
# You can still run them explicitly via node_modules path:
./node_modules/typescript/bin/tsc --version
Fix D – Use junctions instead (advanced)
NTFS junctions (directory hard links) do not require the symlink privilege and work
for directory entries. You cannot tell npm to use junctions automatically, but you can replace
broken .bin symlinks with junctions manually for directory targets:
# CMD — create a junction (no admin required, directories only)
mklink /J "node_modules\.bin\myapp" "node_modules\myapp\bin"
# Compare: /D creates a symbolic directory link (requires privilege)
mklink /D "node_modules\.bin\myapp" "node_modules\myapp\bin" <-- needs Developer Mode
Cause 5 – Corrupted npm Cache
The npm cache stores tarballs and extracted package files in
%APPDATA%\npm-cache (Windows) or ~/.npm (Linux/macOS). A partial
download, a crashed extraction, or a prior EPERM during a cache write can leave corrupt
staging files. Subsequent npm install runs try to use these corrupt entries and
fail with EPERM when they attempt the final rename.
# Step 1: clean the npm cache
npm cache clean --force
# Step 2: verify the cache is clean
npm cache verify
# Step 3: delete node_modules and package-lock.json
# Windows Command Prompt:
rd /s /q node_modules
del package-lock.json
# Windows PowerShell:
Remove-Item -Recurse -Force node_modules
Remove-Item package-lock.json
# Cross-platform (handles long paths and locked files better):
npx rimraf node_modules
npx rimraf package-lock.json
# Step 4: reinstall
npm install
Cause 6 – Read-Only File or Directory
Files checked out from version control with read-only attributes, files on a CD-ROM or
read-only mount, or files manually marked read-only via Windows Explorer properties will throw
EPERM when Node.js tries to write to them. This also affects npm ci which
performs a clean reinstall and needs to delete existing node_modules.
Linux / macOS
# Check permissions on the file or directory
ls -la ./node_modules/some-package/index.js
# Remove the read-only attribute
chmod +w ./node_modules/some-package/index.js
# Recursively fix permissions on node_modules
chmod -R u+w node_modules/
# Or fix ownership if root owns the files (common after sudo npm install)
sudo chown -R $(whoami) node_modules/
Windows
# Remove read-only attribute from a single file
attrib -r "node_modules\some-package\index.js"
# Remove read-only attribute recursively from all files in node_modules
attrib -r /s /d "node_modules\*"
# PowerShell — set full control for current user on node_modules
icacls node_modules /grant "%USERNAME%":F /T
# Take ownership first if needed (run as Administrator)
takeown /f node_modules /r /d y
icacls node_modules /grant "%USERNAME%":F /T
Cause 7 – WSL: Project on NTFS Mount (/mnt/c/...)
Running npm install inside WSL (Windows Subsystem for Linux) on a path under
/mnt/c/ (or any /mnt/ drive) means Node.js is operating on an NTFS
volume accessed via the 9P file system protocol. Symlink creation and certain atomic rename
operations over 9P fail with EPERM because the Windows NTFS driver does not support them
through this protocol path.
# Bad — project is on the NTFS mount
cd /mnt/c/Users/alice/my-project
npm install
# Error: EPERM: operation not permitted, symlink
# Good — project is on the native WSL ext4 filesystem
cd ~/projects/my-project
npm install # works correctly
Moving an existing project into WSL filesystem
# From inside WSL:
mkdir -p ~/projects
cp -r /mnt/c/Users/alice/my-project ~/projects/my-project
cd ~/projects/my-project
# Delete the Windows-side node_modules first (they may have Windows-format bin links)
rm -rf node_modules package-lock.json
npm install # now installs with Linux symlinks
which node — it should return /usr/bin/node or
/home/user/.nvm/versions/node/.../bin/node, not
/mnt/c/Program Files/nodejs/node. Running the Windows npm binary from WSL on a
WSL path is a common source of EPERM and other permission errors.
Cause 8 – npm Cache or Prefix on a Network Share
In corporate environments, Windows roaming profiles often redirect %APPDATA%
to a network share. The default npm cache path (%APPDATA%\npm-cache) ends up on
that network share, which has higher latency and more restrictive locking semantics. Antivirus
on the file server compounds the problem.
# Check where npm is putting its cache and prefix
npm config get cache
npm config get prefix
# If they point to a network share (e.g. \\server\users\alice\...)
# redirect them to a local directory:
npm config set cache "C:\npm-cache"
npm config set prefix "C:\npm-global"
# Create the directories first
mkdir C:\npm-cache
mkdir C:\npm-global
# Add the new prefix to PATH so global binaries remain accessible
# System Properties → Environment Variables → Path → add C:\npm-global\bin
Cause 9 – Windows Long Path Limit (260 characters)
Deeply nested packages in node_modules can exceed Windows' default 260-character
path limit (MAX_PATH). When Node.js or npm tries to create a path beyond this limit, Windows
returns an error that Node.js maps to EPERM (or ENAMETOOLONG on some configurations).
Enable long paths on Windows 10/11
# Option 1: Group Policy (requires local admin)
# Computer Configuration → Administrative Templates →
# System → Filesystem → Enable Win32 long paths → Enabled
# Option 2: Registry (run PowerShell as Administrator)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1
# Option 3: Git-managed repos — enable for git operations
git config --system core.longpaths true
Use rimraf for deletion of long-path node_modules
# rimraf handles long paths and retries on EPERM — safer than rd /s /q
npx rimraf node_modules
# Or install globally once:
npm install -g rimraf
rimraf node_modules
Clean Reinstall Patterns
Windows — full clean reinstall
# Step 1: Close VS Code and kill all node processes
taskkill /IM node.exe /F
taskkill /IM npm.cmd /F
# Step 2: Delete node_modules (use rimraf — handles long paths & retries)
npx rimraf node_modules
# Step 3: Delete the lockfile
del package-lock.json
# Step 4: Clean npm cache
npm cache clean --force
# Step 5: Reinstall
npm install
PowerShell — full clean reinstall
# Kill processes
Get-Process node, npm -ErrorAction SilentlyContinue | Stop-Process -Force
# Remove node_modules and lockfile
Remove-Item -Recurse -Force node_modules -ErrorAction SilentlyContinue
Remove-Item -Force package-lock.json -ErrorAction SilentlyContinue
# Clean cache
npm cache clean --force
# Reinstall
npm install
Linux / macOS — clean reinstall
rm -rf node_modules package-lock.json
npm cache clean --force
npm install
WSL — clean reinstall
# Make sure you are in the WSL filesystem, not /mnt/c/...
pwd # should NOT start with /mnt/
rm -rf node_modules package-lock.json
npm cache clean --force
npm install
Handling EPERM in Code
If your Node.js application performs file system operations that may hit EPERM (for example, writing to a path where another process may hold a lock), catch the error explicitly and distinguish EPERM from other fs errors.
const fs = require('fs/promises');
async function writeFileSafe(filePath, content) {
try {
await fs.writeFile(filePath, content, 'utf8');
} catch (err) {
if (err.code === 'EPERM') {
// Operation not permitted — check for read-only flag or process lock
console.error(
`EPERM writing to ${filePath}. ` +
`Check that no other process holds the file open and that it is not read-only.`
);
// On Windows: check attrib -r, or retry after a short delay
throw err;
}
if (err.code === 'EACCES') {
// Permission bits deny access — chmod or chown the file
console.error(`EACCES writing to ${filePath}. Check file permissions.`);
throw err;
}
throw err; // re-throw all other errors (ENOENT, EMFILE, etc.)
}
}
// Retry helper — useful on Windows where EPERM is often transient
async function writeFileWithRetry(filePath, content, retries = 3, delayMs = 200) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
await fs.writeFile(filePath, content, 'utf8');
return; // success
} catch (err) {
if (err.code === 'EPERM' && attempt < retries) {
console.warn(`EPERM on attempt ${attempt}, retrying in ${delayMs}ms...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
} else {
throw err;
}
}
}
}
Symlink Fix for CI / Docker on Windows
CI pipelines running on Windows agents (GitHub Actions windows-latest,
Azure Pipelines Windows agents) need Developer Mode or the symlink privilege to be
explicitly granted. The cleanest workaround is to avoid symlinks altogether in CI by using
npm ci --ignore-scripts or to enable the privilege in the pipeline definition.
# GitHub Actions — Windows runner, enable Developer Mode equivalent
# The windows-latest runner runs as Administrator, so symlinks work by default.
# If you use a self-hosted Windows runner, add this step:
- name: Enable long paths and symlinks
shell: powershell
run: |
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1
# Grant symlink privilege to current user (requires admin runner)
$acl = Get-Acl "C:\Windows\System32\drivers\etc" # dummy path to load SDDL
Write-Host "Symlinks enabled for: $env:USERNAME"
# Alternative: skip bin symlinks entirely in CI
- run: npm ci --ignore-scripts
# Dockerfile on Windows base image — Node.js symlinks in node_modules
# Use linux containers whenever possible; Windows containers need explicit privilege grants.
# In a Windows container Dockerfile:
FROM mcr.microsoft.com/windows/servercore:ltsc2022
RUN powershell Set-ItemProperty `
-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' `
-Name LongPathsEnabled -Value 1
# ... rest of Dockerfile
Debugging Checklist
- Read
err.syscall—rename/unlink/rmdir= file-handle conflict;symlink= privilege problem;open= read-only or locked file. - Read
err.pathanderr.dest— identifies exactly which file triggered the error. - Close VS Code and all IDEs that have the project open.
- Run
tasklist | findstr node(Windows) orpgrep -l node(Linux/macOS) to find stuck processes; kill them. - Check if the error is for a
symlinksyscall — if so, enable Windows Developer Mode. - Add
node_modulesand%APPDATA%\npm-cacheto Windows Defender exclusions. - Run
npm config get cache— if it points to a network share, redirect to a local directory. - In WSL, run
pwdand confirm you are not on a/mnt/path. - Run
npm cache clean --forcethen deletenode_moduleswithnpx rimraf node_modules. - If all else fails, reboot Windows — this releases all file handles unconditionally — then reinstall.
- On Linux/macOS, check for read-only files:
ls -la node_modules/some-package/; fix withchmod -R u+w node_modules/.
sudo npm install: Running sudo npm install on
Linux/macOS installs files owned by root into node_modules. Subsequent
runs without sudo fail with EACCES or EPERM because the current user cannot
modify root-owned files. Fix with sudo chown -R $(whoami) node_modules/ or
configure a user-level npm prefix.
Frequently Asked Questions
What is EPERM: operation not permitted in Node.js?
EPERM stands for Error: operation not PERMitted. It is a POSIX system error (errno 1) returned by the OS kernel when a file system operation is refused not because of file permission bits (that is EACCES), but because the operation itself is disallowed — for example, creating a symlink without the required Windows privilege, renaming a file that another process has open, or writing to a read-only filesystem.
What causes EPERM: operation not permitted during npm install on Windows?
The most common causes are: (1) VS Code or another IDE holding a file handle in node_modules while npm tries to rename a staging file; (2) Windows Defender or antivirus locking a file it is scanning at the moment npm renames it; (3) a leftover node.exe process from a crashed prior run; (4) npm trying to create symlinks in .bin without SeCreateSymbolicLinkPrivilege; (5) a corrupted npm cache. Fix: close the IDE, kill node.exe processes, add antivirus exclusions, then do a clean reinstall.
How do I delete node_modules on Windows when I get EPERM?
Use npx rimraf node_modules — rimraf handles long paths and retries on locked files automatically. Alternatively: Remove-Item -Recurse -Force node_modules (PowerShell) or rd /s /q node_modules (Command Prompt). If all three fail, close VS Code, kill all node.exe processes in Task Manager, wait 10 seconds, then retry. A reboot is the last resort — it unconditionally releases all file handles.
How do I fix EPERM symlink errors without running as Administrator?
Enable Windows Developer Mode: Settings → System → For developers → Developer Mode → On. This grants the SeCreateSymbolicLinkPrivilege to your user account without requiring a UAC prompt on each npm install. As a workaround (not a full fix), you can also run npm install --no-bin-links to skip creating symlinks in .bin, but then CLI tools from packages will not be directly runnable as commands.
What is the difference between EPERM and EACCES in Node.js?
EACCES (permission denied) means the process lacks the read, write, or execute permission bits on a specific file or directory — a chmod/chown problem. EPERM (operation not permitted) means the operation itself is forbidden in the current OS context regardless of file permissions — for example, creating a symlink without the required Windows privilege, renaming an open file, or writing to a read-only filesystem. On Windows, EPERM is used broadly for many kernel-level refusals that POSIX would distinguish more granularly.
Why does EPERM happen in WSL during npm install?
In WSL, EPERM occurs when the project is on an NTFS mount (/mnt/c/...) rather than the native WSL ext4 filesystem. Symlink creation and atomic renames over the 9P NTFS protocol fail with EPERM. Fix: move your project to the WSL home directory (~/projects/my-project), delete node_modules and package-lock.json, then run npm install again. Also ensure you are using the WSL Linux Node.js installation, not the Windows one — check with which node.
How do I add node_modules to Windows Defender exclusions?
Via GUI: Windows Security → Virus & threat protection → Manage settings → Exclusions → Add or remove exclusions → Add an exclusion → Folder → select your project root (or the node_modules subfolder). Also exclude %APPDATA%\npm-cache. Via PowerShell (run as Administrator): Add-MpPreference -ExclusionPath "C:\your\project\node_modules" and Add-MpPreference -ExclusionPath "$env:APPDATA\npm-cache".
Why does EPERM happen in CI on Windows but not locally?
Common CI-specific causes: (1) the CI Windows agent user account does not have the symlink privilege — grant it or use npm ci --ignore-scripts; (2) the agent runs antivirus that is not configured with exclusions; (3) the project path is long and exceeds the 260-character limit — enable long paths with the registry key or Group Policy; (4) the npm cache is on a network share — set a local cache path with npm config set cache C:\npm-cache in the pipeline.
Related Errors
ENOENT: no such file or directory— path not found; npm ENOENT during install usually means node_modules is missing, not a permissions issueEMFILE: too many open files— process exceeded OS file descriptor limit; often occurs alongside EPERM when node_modules is very largeEACCES: permission denied— file-level permission bit refusal; usechmod/chownto fix rather than the process-kill approach needed for EPERMEBUSY: resource busy or locked— the file or device is busy; similar to EPERM but indicates the resource is actively in use rather than the operation being prohibitedENOTEMPTY: directory not empty— rmdir fails because the directory has contents; use rimraf orrm -rfinstead