← Back to DevBytes

Fix 'npm ERR! code EACCES' Permission Error: Complete Troubleshooting Guide

Understanding the npm ERR! code EACCES Permission Error

When working with Node.js and npm, one of the most frustrating roadblocks you'll encounter is the dreaded EACCES (Error Access) permission error. This error occurs when npm tries to read from or write to a directory for which the current user doesn't have the necessary permissions. It typically manifests when installing packages globally or when npm itself tries to access its own cache directories. Understanding why this error happens is the first step toward fixing it permanently.

What Exactly Triggers This Error?

The EACCES error stems from the Unix filesystem permission model. When you install Node.js via a package manager like apt (Ubuntu/Debian) or yum (RHEL/CentOS), or use the official installer from nodejs.org, the installation often places npm's global modules directory (/usr/local/lib/node_modules or /usr/lib/node_modules) and its cache directory under root ownership. When a non-root user subsequently runs npm install -g or even a regular npm install that touches the cache, npm lacks permission to write to these system-level directories, and the process fails with:

npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules/your-package
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/your-package'
npm ERR!  [Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/your-package']

You might also see variations involving syscall access, syscall open, or syscall rename, but the root cause is always the same: the user account running npm lacks write access to the target path.

Why This Matters

Left unresolved, this error completely blocks your ability to install global npm packages — tools like create-react-app, typescript, nodemon, eslint, or any CLI utility you rely on daily. Even local project installations can fail if npm's cache directory has incorrect permissions. Beyond the immediate frustration, developers often resort to a dangerous quick fix: running npm with sudo. This grants root privileges to an entire installation process, potentially executing malicious or buggy post-install scripts with full system access. Fixing the EACCES error properly is therefore not just about convenience — it's a security imperative.

Method 1: Use a Node Version Manager (Recommended)

The cleanest, most sustainable solution is to bypass system-level Node.js installations entirely and use a version manager. Tools like nvm (Node Version Manager) install Node.js and npm entirely within your home directory (~/.nvm), where you naturally have full read and write permissions. This eliminates permission conflicts permanently.

Installing nvm and Node.js

First, install nvm using the official install script:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

Alternatively, use wget:

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

After installation, close and reopen your terminal, or source your shell profile:

source ~/.bashrc
# or
source ~/.zshrc
# or
source ~/.profile

Verify nvm is available:

nvm --version

Now install the latest stable Node.js release (which includes npm):

nvm install --lts
nvm use --lts

Check that npm's global prefix now lives safely under your home directory:

npm config get prefix
# Output should be something like: /home/your-username/.nvm/versions/node/v20.11.0

From this point forward, any global install will succeed without sudo:

npm install -g typescript
# Installs cleanly with zero permission errors

Using nvm on an Existing System with Prior Permission Issues

If you previously installed packages globally using sudo and now want to migrate to nvm, it's wise to clear out the old system-level global packages to avoid confusion. First, list what was installed globally:

npm ls -g --depth=0

Then, after switching to nvm's Node.js, reinstall those packages cleanly:

nvm install --lts
nvm use --lts
npm install -g create-react-app nodemon eslint typescript

The old system-level packages will remain on disk but won't interfere with nvm-managed versions. You can optionally remove them later using the original system Node.js (if still present), but it's not strictly necessary.

Method 2: Manually Fix npm's Default Directory Permissions

If you prefer to keep the system-installed Node.js (for example, in a managed server environment where nvm isn't practical), you can reconfigure npm to use a directory inside your home folder. This is the approach officially recommended in npm's documentation.

Step 1: Create a User-Level Global Packages Directory

mkdir -p ~/.npm-global

Step 2: Tell npm to Use This Directory as Its Prefix

npm config set prefix '~/.npm-global'

Verify the change took effect:

npm config get prefix
# Should output: /home/your-username/.npm-global

Step 3: Add the New Bin Directory to Your PATH

Global packages install their executables into ~/.npm-global/bin. You need to ensure this directory is in your shell's PATH environment variable. Add the following line to your ~/.bashrc, ~/.zshrc, or ~/.profile:

export PATH="$HOME/.npm-global/bin:$PATH"

Reload your shell configuration:

source ~/.bashrc

Now test a global installation:

npm install -g nodemon
nodemon --version
# Should output the version number without errors

Step 4 (Optional): Fix Cache Directory Permissions

Sometimes the npm cache directory itself has root-owned files from previous sudo invocations. You can either reset the cache entirely or fix its ownership. To reset:

npm cache clean --force

To fix ownership of the existing cache without deleting it:

sudo chown -R $(whoami) ~/.npm

After this, even local npm install commands that interact with the cache should work flawlessly.

Method 3: Fix Permissions on System Directories (Quick but Not Ideal)

If you're in a hurry and understand the security implications, you can directly correct the ownership of npm's system directories. This approach works but is less recommended because future system updates or new global package installations could reintroduce the problem.

Identify the Problematic Directories

First, find npm's global prefix and cache locations:

npm config get prefix
npm config get cache

Typical locations on Linux systems:

Change Ownership to Your User

# Change ownership of the global node_modules directory
sudo chown -R $(whoami) /usr/local/lib/node_modules

# Change ownership of the directory where global binaries are linked
sudo chown -R $(whoami) /usr/local/bin

# If npm's cache is under root's home, move it or fix it
sudo chown -R $(whoami) ~/.npm

Important caveat: The /usr/local/bin directory contains many non-npm binaries (like Python, pip, or other system tools). Changing its ownership wholesale is overly broad. A more targeted approach is to fix only the symlinks npm creates:

# Find npm-related symlinks in /usr/local/bin and fix them individually
ls -l /usr/local/bin | grep 'node_modules'
# Then chown each symlink's target, not /usr/local/bin itself

For this reason, Method 1 or 2 is strongly preferred over blanket ownership changes.

Method 4: Diagnose and Fix Specific File/Directory Permission Errors

Sometimes the error is not about the global prefix but about a specific local project or a nested dependency. In these cases, a surgical approach works best.

Check Ownership of Your Project's node_modules

If a local npm install fails with EACCES inside a project directory, check who owns node_modules:

ls -la node_modules/
# If root owns any files, you'll see "root" in the owner column

Fix ownership recursively:

sudo chown -R $(whoami) node_modules/
# Then retry
npm install

Check for Lock Files Owned by Root

Root-owned package-lock.json or yarn.lock files can also cause failures when npm tries to rewrite them:

ls -l package-lock.json
# If owner is root:
sudo chown $(whoami) package-lock.json
npm install

Global Cache File Corruption

A corrupted or root-owned cache file can cause EACCES even in local installs. Clear the cache entirely:

npm cache clean --force
# Also remove the cache directory if it exists under root ownership
sudo rm -rf ~/.npm/_cacache
# Then let npm recreate it with correct permissions
npm install

Method 5: Resolving EACCES When Using Docker or CI/CD Pipelines

In containerized environments, the EACCES error often appears when a Dockerfile runs npm as root initially but later switches to a non-root user for security. The cache and global directories end up with mixed ownership.

Docker Best Practice: Run npm as the Target User

FROM node:20-alpine

# Create a non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Create the app directory and set ownership BEFORE running npm
WORKDIR /home/appuser/app
RUN chown -R appuser:appgroup /home/appuser/app

# Switch to non-root user
USER appuser

# Copy package files and install as the non-root user
COPY --chown=appuser:appgroup package*.json ./
RUN npm install --loglevel warn

# Copy the rest of the application
COPY --chown=appuser:appgroup . .

CMD ["npm", "start"]

This ensures all node_modules and cache artifacts are owned by the application user from the start.

Fixing an Existing Container Build

If you're dealing with an existing Dockerfile that runs npm install as root and then fails when switching users, you can add a permission fix step:

# After running npm install as root, fix ownership before switching users
RUN npm install --production
RUN chown -R appuser:appgroup node_modules /home/appuser/.npm

However, restructuring the Dockerfile to run npm as the final user (as shown above) is the cleaner long-term solution.

Method 6: Using sudo — The Last Resort (With Guardrails)

Using sudo to run npm commands is strongly discouraged because it grants root privileges to package installation scripts. However, in tightly controlled, ephemeral environments (like a disposable VM for a single build), it may be acceptable if you take precautions.

If you absolutely must use sudo, limit its scope:

# Only for a specific global install, not for every npm command
sudo npm install -g some-trusted-package

# Immediately revoke root-owned artifacts afterward
sudo chown -R $(whoami) ~/.npm
sudo chown -R $(whoami) /usr/local/lib/node_modules

Never run npm install (local) with sudo. Never run npm publish with sudo. Never run third-party package installation scripts with elevated privileges unless you've audited the source code thoroughly. The convenience is never worth the risk.

Best Practices to Prevent EACCES Errors Permanently

Quick Diagnostic Commands Summary

Bookmark these commands for rapid troubleshooting when you encounter an EACCES error:

# 1. Check npm's current prefix (global install location)
npm config get prefix

# 2. Check npm's cache directory
npm config get cache

# 3. List global packages (helps identify what needs reinstalling)
npm ls -g --depth=0

# 4. Find who owns the global node_modules
ls -la $(npm config get prefix)/lib/node_modules

# 5. Check ownership of local node_modules
ls -la node_modules/

# 6. Check ownership of package-lock.json
ls -l package-lock.json

# 7. Clean the cache
npm cache clean --force

# 8. Fix user ownership of npm's entire home-directory footprint
sudo chown -R $(whoami) ~/.npm

# 9. Verify the PATH includes your global bin directory
echo $PATH | tr ':' '\n' | grep npm

Understanding the Error Anatomy

When you see the full error stack, each line gives you a clue:

npm ERR! code EACCES           ← Permission denied error code
npm ERR! syscall mkdir         ← The specific system call that failed
npm ERR! path /some/directory  ← The exact path npm couldn't write to
npm ERR! errno -13             ← Linux errno 13 = EACCES

The syscall value tells you what operation failed — mkdir (creating a directory), open (opening a file for writing), rename (moving a file), or access (checking existence/permissions). The path pinpoints exactly which directory or file has incorrect ownership. Use these two fields to apply the targeted fixes described in Method 4.

Conclusion

The npm ERR! code EACCES permission error is a signal that your Node.js development environment has a fundamental configuration issue — not a bug in your code or npm itself. By understanding the Unix permission model that underlies the error, you can choose the right fix for your context: a version manager for personal development machines, a user-space prefix for shared servers, targeted ownership fixes for existing projects, or proper Docker layering for containerized workflows. The key takeaway is simple: npm should never need root privileges to do its job. Investing a few minutes to configure a user-level installation path — whether through nvm, a custom prefix, or a dev container — pays dividends in security, stability, and friction-free development for years to come. Fix it once, fix it right, and never type sudo npm install again.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles