TypeScript

npm EACCES without sudo: fix the cache and global prefix

Reproduced on npm 11.19.0: how to tell the cache EACCES from the global-install EACCES by the path line, and the two fixes npm documents that end sudo for good.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

TL;DR#

EACCES from npm means the operating system refused a write because the folder belongs to another user, almost always root. There are two different folders behind it, and the path line of the error tells you which one:

  • Your cache, ~/.npm. A previous sudo npm … left root-owned files there. Give the folder back to yourself once, with the chown command npm prints, and stop using sudo.
  • The global folder, /usr/local/lib/node_modules. Node.js was installed system-wide, so npm install -g writes into a root-owned prefix. Install Node.js with a version manager such as nvm (npm's recommended fix), or set the npm prefix to a folder in your home directory and add its bin to PATH.

Both cases were reproduced on 11 October 2026 in the official node:24 Docker image (Node.js 24.21.0, npm 11.19.0) as the image's non-root user.

The error#

The Stack Overflow question dates from npm 1.2.18 on macOS (System Darwin 12.2.0). Every npm search or npm install failed unless it was run with sudo:

text
npm ERR! Error: EACCES, open '/Users/chietala/.npm/-/all/.cache.json'
npm ERR! Please try running this command again as root/Administrator.
npm ERR! code EACCES

Current npm still fails on the same causes, but it now prints a diagnosis. When the failing path is inside the cache, npm 11.19.0 prints this:

text
npm error code EACCES
npm error syscall open
npm error path /home/node/.npm/_cacache/tmp/***
npm error errno EACCES
npm error Your cache folder contains root-owned files, due to a bug in previous versions of npm which has since been addressed.
npm error To permanently fix this problem, please run:
npm error   sudo chown -R 1000:1000 "/home/node/.npm"

When the failing path is the global folder, you get the generic message:

text
npm error code EACCES
npm error syscall mkdir
npm error path /usr/local/lib/node_modules/npm-check-updates
npm error errno -13
npm error Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/npm-check-updates'
npm error The operation was rejected by your operating system.
npm error It is likely you do not have the permissions to access this file as the current user

Why npm needs root in the first place#

npm writes to two places, and either one can be owned by root.

The cache. The npm folders documentation says cache files are stored in ~/.npm on POSIX systems. If the first npm command that ever touches that folder runs as root, root owns what it creates. The reproduction ran npm as root with HOME set to the normal user's home, as a stand-in for sudo (the Docker image has no sudo). On a fresh cache it left everything root-owned:

text
drwxr-xr-x 5 0 0 4096 Oct 11 01:55 _cacache
drwxr-xr-x 2 0 0 4096 Oct 11 01:55 _logs

That happened on npm 11.19.0, even though npm's own message calls this "a bug in previous versions of npm which has since been addressed". The next ordinary npm install in a project failed with the cache message above. npm picks that message in lib/utils/error-message.js: on a non-Windows system, an EACCES or EPERM whose path starts with the configured cache gets the chown advice, and anything else gets the generic "rejected by your operating system" text.

The global prefix. The same folders page says the prefix config "defaults to the location where node is installed. On most systems, this is /usr/local." On Unix, global packages go to {prefix}/lib/node_modules and their commands are linked into {prefix}/bin. An installer that puts Node.js into /usr/local as root leaves both folders owned by root:

text
/usr/local
drwxr-xr-x 1 root root 4096 Sep  9 18:47 /usr/local/bin
drwxr-xr-x 4 root root 4096 Sep  7 23:56 /usr/local/lib/node_modules

A local npm install inside your own project never touches the prefix. In the reproduction it worked without sudo once the cache was fixed, while the default prefix was still /usr/local. Only -g installs (and npm link) write there. Answer 2 on the Stack Overflow thread puts it this way: "You probably ran Node.js installation with root permissions, that's why the global package installation is asking you to be root."

Fix#

Fix the cache first if the path line points into ~/.npm, then pick one of the two ways to stop global installs from needing root. The npm guide on EACCES errors lists those two ways and recommends the version manager.

1. Give the cache back to your user#

This is the command npm prints itself. It is the one place where you still need sudo, once:

bash
sudo chown -R "$(id -u):$(id -g)" ~/.npm

In the reproduction the same project install went through straight afterwards:

text
added 1 package, and audited 2 packages in 612ms

This does not fix global installs. Immediately after the chown, npm install -g still failed with EACCES: permission denied, mkdir '/usr/local/lib/node_modules/npm-check-updates', because the cache was never the cause of that one.

The npm guide calls reinstalling npm with a node version manager "the best way to avoid permissions issues". It also says you do not need to remove your current Node.js first. With nvm, Node.js and every global package live under ~/.nvm, which you own. Install nvm with the command from its README, open a new terminal, then:

bash
nvm install 24
npm config get prefix
npm install -g npm-check-updates
command -v ncu

The prefix now points inside your home directory, and the global install needs no sudo. The reproduction used nvm v0.40.8 and ran these commands after removing the prefix left by the test in step 3 (see the nvm section below):

text
/home/node/.nvm/versions/node/v24.21.0
added 1 package in 324ms
/home/node/.nvm/versions/node/v24.21.0/bin/ncu

3. Without a version manager: point the prefix at a folder you own#

The npm guide's manual route keeps the Node.js you already have. It tells npm to install global packages into ~/.local:

bash
npm config set prefix ~/.local

That writes one line to ~/.npmrc (prefix=/home/node/.local in the reproduction). Then add the new bin folder to PATH in ~/.profile:

bash
PATH=~/.local/bin:$PATH

The guide adds that zsh users also need source ~/.profile in ~/.zprofile. Reload the profile in the current terminal:

bash
source ~/.profile

Skip the PATH step and the install succeeds, but the command is not found. This is what the reproduction printed between the two steps:

text
bash: line 1: ncu: command not found

Verify the fix#

Run these as yourself, without sudo:

bash
npm config get prefix
npm install -g npm-check-updates
ncu --version
command -v ncu

The prefix should be inside your home directory, the install should finish with "added 1 package", and command -v should print a path under that prefix. With the ~/.local prefix, the reproduction printed:

text
added 1 package in 6s
23.1.1
/home/node/.local/bin/ncu

Nothing was written to the system folder: ls -ld /usr/local/lib/node_modules/npm-check-updates answered "No such file or directory". If the error is still EACCES, read its path line again. A path inside ~/.npm means step 1 was skipped or ran on the wrong folder. A path under /usr/local means npm is still using the old prefix, so check npm config get prefix and which npm.

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

Chowning /usr/local/lib/node_modules is half a fix#

The third answer on the thread suggests sudo chown -R $USER /usr/local/lib/node_modules. On its own it moves the failure one step further. After that chown, with the default prefix, the reproduction failed on the command link instead:

text
npm error syscall symlink
npm error path ../lib/node_modules/npm-check-updates/build/cli.js
npm error dest /usr/local/bin/ncu
npm error Error: EACCES: permission denied, symlink '../lib/node_modules/npm-check-updates/build/cli.js' -> '/usr/local/bin/ncu'

In the official Debian-based image, /usr/local/bin is still owned by root after that chown, and npm left nothing behind in node_modules. Wherever that folder belongs to root, the same failure follows. Taking ownership of both system folders is not one of the routes npm documents. A user prefix or a version manager gets the same result without changing system folders.

nvm refuses a prefix in ~/.npmrc#

The two fixes do not combine. If you set a prefix first and switch to nvm later, the prefix line left in ~/.npmrc makes nvm stop. In the reproduction, nvm install 24 printed this (loading nvm.sh in a new shell printed the same message, with --silent added to the suggested command):

text
Your user’s .npmrc file (${HOME}/.npmrc)
has a `globalconfig` and/or a `prefix` setting, which are incompatible with nvm.
Run `nvm use --delete-prefix v24.21.0` to unset it.

The check lives in nvm.sh. It also rejects a PREFIX or NPM_CONFIG_PREFIX environment variable that points outside nvm's own folders. Run the command it prints. It deletes the prefix line, after which the reproduction showed:

text
Now using node v24.21.0 (npm v11.19.0)

Remove the ~/.local/bin line from your profile at the same time: under nvm, global commands are linked into the bin folder of the active Node.js version, so that line no longer serves anything.

Windows, Docker and the next error#

The prefix section of the npm guide "does not apply to Microsoft Windows". There the default prefix is %AppData%\npm, which is already inside your user profile. npm's Windows message for this error code says the file may be "already in use (by a text editor or antivirus), or that you lack permissions to access it", so check for a program holding the file before you look at permissions.

Inside a Dockerfile, the cause is usually an install that ran as root before a USER switch, and the fix is different. Fix EACCES: permission denied, mkdir node_modules/.cache covers that case. For tools you only use in one project, you do not need a global install at all. Install the tool locally and run it through an npm script, as in Fix tsc is not recognized: run the local compiler. On macOS, once permissions are fixed, a global package with a native addon can stop at the next hurdle. If the install now ends with gyp: No Xcode or CLT version detected!, continue with the node-gyp Xcode fix.

The thread dates from 2013, but the cause has not changed: a folder npm writes to belongs to root. Fix the cache once, move global installs to a folder you own, and stop typing sudo in front of npm.

Further reading: Fix MikroORM: global EntityManager instance is disallowed. Also see Fix npm EACCES: permission denied on package-lock.json.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote