The error, verbatim
Jump to the fix ↓npm error code EACCES
npm error syscall mkdir
npm error path /opt/rootnode/lib/node_modules/cowsay
npm error errno -13
npm error Error: EACCES: permission denied, mkdir '/opt/rootnode/lib/node_modules/cowsay'
Tested on
- Node / npm
- 24.15.0 / 11.x; node:24-slim image
- OS
- Ubuntu 24.04 (GitHub Actions runner), Docker
Contents
EACCES means your user isn’t allowed to write where npm is trying to write. The path line says where that is, and in my tests there were three places it came from:
path in the error |
syscall |
What happened |
|---|---|---|
/usr/.../lib/node_modules/<package> (Node’s own folder) |
mkdir |
npm install -g as a normal user |
~/.npm/_cacache/... |
open |
sudo npm ran earlier and left root-owned files in your cache |
/app/node_modules |
mkdir |
Docker: USER node, but the folder belongs to root |
The fix is different for each, so match your path first.
Global installs: npm install -g
I put Node in a folder owned by another user, the way a system install leaves it under root, and ran npm install -g cowsay as a normal user:
npm error code EACCES
npm error syscall mkdir
npm error path /opt/rootnode/lib/node_modules/cowsay
npm error Error: EACCES: permission denied, mkdir '/opt/rootnode/lib/node_modules/cowsay'
npm config get prefix showed why: global packages go next to Node itself, and that folder isn’t yours.
Give npm a global folder in your home directory, and put its bin on your PATH:
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrcUse ~/.zshrc instead of ~/.bashrc if your shell is zsh. A Node version manager such as nvm does the same thing for you: it installs Node inside your home folder, so global installs never need root.
Your cache folder contains root-owned files
After one sudo npm install, the next normal npm install in any project failed, this time on the cache:
npm error code EACCES
npm error syscall open
npm error path /home/dev/.npm/_cacache/index-v5/98/20/8cf39748e26add...
npm error Your cache folder contains root-owned files, due to a bug in previous versions of npm which has since been addressed.
Despite the wording, this happened on a current npm. Running npm as root was enough.
The error’s last line prints the fix: give your cache back to your user.
sudo chown -R $(id -u):$(id -g) ~/.npmThen stop using sudo with npm. The global-folder fix above is what makes that possible.
Docker: EACCES mkdir ‘/app/node_modules’
The official Node images have a node user, and switching to it is good practice. But WORKDIR /app creates the folder as root, so npm install after USER node can’t write in it:
npm error code EACCES
npm error syscall mkdir
npm error path /app/node_modules
npm error Error: EACCES: permission denied, mkdir '/app/node_modules'
Hand the folder to node before switching users, and copy files in with the same owner:
FROM node:24-slim
WORKDIR /app
RUN chown node:node /app
USER node
COPY --chown=node:node package.json .
RUN npm installHow this was tested
Ubuntu 24.04 on a GitHub Actions runner, October 22, 2026. A new user without root ran npm against a Node 24.15.0 install in a folder it couldn’t write to, before and after setting a user prefix. The same user’s cache was then touched once by sudo npm install, and the next install was run as the user. The Docker case built a node:24-slim image with USER node, with and without changing the owner of /app. I didn’t test macOS. Every message above is the real output, from the test script.
— N.K., end of entry No.086