EPERM: operation not permitted is Node.js reporting that the operating system rejected a filesystem operation for permission reasons — but unlike the more specific EACCES, EPERM is a broader catch-all that shows up for reasons ranging from genuine permission restrictions to Windows-specific file locking to antivirus software interfering with file access.
This error means the underlying operating system call for a filesystem operation (writing, deleting, renaming a file, or similar) was rejected — the specific cause varies more than most Node.js filesystem errors, and the fix genuinely depends on your platform and what operation triggered it.
Why This Error Happens
EPERM is thrown when the OS-level system call underlying a Node.js filesystem operation returns a permission-related failure. On Unix-like systems, this usually means a genuine permissions issue (trying to modify a file or directory you don't have write access to, or trying to perform an operation only root can do). On Windows, EPERM frequently appears for file locking reasons — another process (including antivirus software, a code editor, or a previous unclosed handle in your own process) has the file open, and Windows enforces exclusive access differently than Unix does.
Reproducing and Diagnosing the Error
A common cross-platform case — trying to delete or rename a file that's still open elsewhere:
const fs = require("fs");
fs.writeFileSync("output.log", "some data");
const stream = fs.createWriteStream("output.log", { flags: "a" });
// stream still open, holding a handle
fs.unlinkSync("output.log");
// Error: EPERM: operation not permitted, unlink 'output.log'
// (especially common on Windows, where an open handle blocks deletion)
Checking actual file permissions on Unix systems:
ls -la ./output.log
# Verify the running process's user actually has write permission
Core Concepts Behind This Error
On Windows, EPERM often means a file handle is still open somewhere, not that permissions are genuinely misconfigured — Windows enforces file locking more strictly than Unix, so an unclosed write stream, a file open in another program, or an antivirus scan in progress can all produce this error for an operation that would succeed once the handle is released.
On Unix-like systems, EPERM more often reflects an actual permissions problem — attempting to write to a directory owned by another user, modify a file without write permission, or perform an operation requiring elevated privileges — and the fix is adjusting permissions or running with appropriate access, not a retry.
Global npm package installs are a common source of EPERM on systems where the global npm directory requires elevated permissions to write to — this is specifically a permissions configuration issue with the npm setup, distinct from application-level filesystem code.
Retrying after a short delay resolves transient Windows file-locking cases (where another process briefly held the file), but is not a fix for genuine Unix permission issues, which will fail identically on every retry — distinguishing which category you're in determines whether a retry is a reasonable mitigation or wasted effort.
Fixing "EPERM: Operation Not Permitted"
Fix 1: Ensure file handles and streams are properly closed before attempting to delete, rename, or move the file (particularly relevant on Windows):
const stream = fs.createWriteStream("output.log");
stream.write("data");
stream.end(() => {
// Only attempt unlink after the stream is confirmed closed
fs.unlinkSync("output.log");
});
Fix 2: For genuine Unix permission issues, adjust ownership or permissions rather than working around the error in code:
chmod u+w ./target-directory
# or, if genuinely needed and understood:
sudo chown $(whoami) ./target-directory
Fix 3: For npm global install EPERM errors, reconfigure npm's global directory to one your user owns, rather than running npm with elevated privileges (which introduces its own security concerns):
npm config set prefix ~/.npm-global
# then add ~/.npm-global/bin to your PATH
Fix 4: For transient Windows file-locking cases, implement a short retry with backoff specifically for the operation that's failing:
async function unlinkWithRetry(path: string, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
fs.unlinkSync(path);
return;
} catch (err: any) {
if (err.code !== "EPERM" || i === attempts - 1) throw err;
await new Promise((r) => setTimeout(r, 200));
}
}
}
Should You Just Run Your Process With Elevated Permissions to Avoid EPERM?
Almost never — running with sudo or as Administrator to work around an EPERM masks the actual underlying permission structure issue rather than fixing it, and introduces real security risk, especially for anything running in production or handling untrusted input. Fix the actual ownership/permission configuration, or close open handles properly, rather than reaching for elevated privileges as a blanket workaround.
Preventing This Error in Production
Ensure your application properly closes file handles and streams before performing operations (delete, rename, move) that require exclusive access to the same file, particularly important for cross-platform applications that also need to run correctly on Windows. Configure filesystem permissions and ownership deliberately for any directories your application writes to, rather than relying on elevated privileges as a workaround for a permissions structure that doesn't actually fit your application's needs.
If you hit this error, check your platform first — Windows and Unix causes differ substantially, and the fix (closing handles vs. adjusting permissions) depends entirely on which situation you're actually in.