Quick fix
# One-liner (form 1, recommended): move the global prefix to your user directory, permanent fix
npm config set prefix ~/.npm-global && export PATH="$HOME/.npm-global/bin:$PATH" && npm install -g <pkg>
For form 2: sudo chown -R $(whoami) ~/.npm.
What you will run into
npm install -g <pkg> reports npm ERR! code EACCES; you think "permission issue, add sudo" — and next time even the cache can't be written (Your cache folder contains root-owned files), making it worse.
The error actually has two forms with different paths and completely different fixes — check where path points before acting.
Why this happens
npm global installs write to two locations: the global package directory (npm prefix -g, e.g. /usr/local/lib/node_modules) and the cache (~/.npm):
| Form | path points to | Root cause |
|---|---|---|
| Form 1 | /usr/local/lib/node_modules/... | Global dir owned by root, current user isn't root |
| Form 2 | /home/<user>/.npm/_cacache | Used sudo npm install before; ~/.npm got mixed with root-owned files |
Form 1 is the norm for non-root users installing global packages (system node lives in /usr/local or /usr); form 2 is the after-effect of having used sudo before. Both errors carry EACCES + errno -13, but the fixes differ.
Global npm installs write to two locations: the global package directory (node_modules under npm prefix -g) and the cache (~/.npm).
EACCES means "the current user cannot write to the target directory": form 1's global directory is owned by root (system node lives in /usr/local or /usr), so non-root writes are rejected;
form 2 comes from a historical sudo npm install that made root the owner of ~/.npm cache files, after which normal-user cache writes fail.
Both forms show EACCES/errno -13, but the causes and fixes differ completely — form 1 is "directory ownership", form 2 is "cache ownership poisoned".
Deterministic takeaway: an error path containing node_modules = form 1; containing _cacache/.npm = form 2;
the correct fix for form 1 is npm config set prefix ~/.npm-global (move the global dir into the user home and add to PATH — do not force-install with sudo), and for form 2 sudo chown -R $(whoami) ~/.npm (restore cache ownership);
fixed diagnostic: ls -ld the target directory and compare with the error path.
Step by step
1. Read the error's path to identify the form — Diagnose
npm install -g yaml 2>&1 | grep -E "path|EACCES"
path /usr/local/lib/... → form 1; path /home/<user>/.npm/_cacache → form 2.
2. Form 1: move the global prefix to your user directory (recommended) — Required
npm config set prefix ~/.npm-global
export PATH="$HOME/.npm-global/bin:$PATH"
Add export PATH=... to ~/.bashrc so global packages install into your own directory from now on, never touching system dirs.
3. Form 2: give ~/.npm back to yourself — Required
sudo chown -R $(whoami) ~/.npm
npm's error message usually suggests the matching chown command. If you don't care about old cache, rm -rf ~/.npm and let it rebuild.
4. Alternatives — Advanced
- Manage node with nvm: node lives under
~/.nvm, global packages are naturally writable; sudo npm install -gworks but isn't recommended — it keeps manufacturing form 2.
How to confirm it works
npm install -g yaml && yaml --help
No more EACCES and yaml runs (prints usage) means success. Measured: after form-1 fix added 1 package; after form-2 chown changed 1 package.
Other environments
| Environment | Notes |
|---|---|
| Linux (system node in /usr/local) | Form 1 common; move prefix to ~/.npm-global |
| WSL Ubuntu | Same as native Linux; leftover sudo also manufactures form 2 |
| macOS (Homebrew node) | Dir owned by brew user; nvm or prefix change recommended too |
| Used sudo before | Always check ~/.npm ownership; chown -R $(whoami) ~/.npm |
Symptom
npm install -g <pkg> fails with a permission error, in two typical forms:
# Form 1: no write permission on the global package dir (most common)
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules/yaml
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/yaml'
# Form 2: cache dir owned by root (after using sudo)
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /home/<user>/.npm/_cacache
npm ERR! Your cache folder contains root-owned files, due to a bug in
npm ERR! previous versions of npm which has since been addressed.
Key traits: both forms carry npm ERR! code EACCES + errno -13; exit code is usually 243; Form 1 happens for non-root users installing globally (system node under /usr/local or /usr, dirs owned by root); Form 2 is common after ever running sudo npm install (root-owned files mixed into ~/.npm, regular users can no longer write).
Cause
Global installs write two locations: the global package dir (npm prefix -g, e.g. /usr/local/lib/node_modules) and the cache (~/.npm). The former is root-owned while your user isn't root → Form 1; the latter got root-owned files/dirs (sudo leftovers) → Form 2. Same EACCES, different paths, different fixes — check what path says first.
Solution
- Form 1: move the global prefix into your home (recommended, one-time):
npm config set prefix ~/.npm-global
export PATH="$HOME/.npm-global/bin:$PATH" # add to ~/.bashrc
Global installs now go to your own directory and never touch system dirs.
- Form 1 alternatives: manage node with nvm (node lives in
~/.nvm, global dirs are naturally writable); orsudo npm install -g(works but not recommended — it creates Form 2). - Form 2: give
~/.npmback to yourself:
sudo chown -R $(whoami) ~/.npm
(npm's own error suggests the exact chown command.) If you don't need the old cache, rm -rf ~/.npm and let it rebuild.
- Verify:
npm install -g <pkg>succeeds and the command runs (e.g.yamlprints usage).
Verification Evidence
- Local repro (2026-08-29, WSL Ubuntu, node v22.22.1 / npm 9.2.0, global prefix /usr/local; temporary user <user>):
- Form 1 repro: <user> runs
npm install -g yaml→EACCES: permission denied, mkdir '/usr/local/lib/node_modules/yaml'(exit 243); - Form 1 fix:
npm config set prefix ~/.npm-global+ PATH → same commandadded 1 package,yamlruns (usage output); - Form 2 repro: chown
/home/<user>/.npmto root → install again →EACCES ... /home/<user>/.npm/_cacache, npm itself suggestssudo chown -R 1002:1002 "/home/<user>/.npm"(exit 243); - Form 2 fix:
chown -R <user>:<user> /home/<user>/.npm→changed 1 packagesuccess; - Temporary user and cache removed afterwards (no leftovers in the test environment).
Evidence details kept locally (2026-08-29)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
npmBundled solutions (combos)
This problem is part of the following verified bundles.
Source & evidence
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk