npm EACCES: Permission Denied

EACCES means the operating system refused npm access to a specific file or directory. The denied path is the most important clue. A failure under the global npm prefix is a different problem from a root-owned project file, a cache directory, or a container/CI mount. Read the path first, confirm who owns it and which user runs npm, then make the narrowest ownership or installation change that fits that path.

Node.js · npm · EACCESpermissions · ownership · prefixLast reviewed Oct 29, 2026 · Published Oct 11, 2026
🎯 You’re likely in the right place if:
npm prints npm ERR! code EACCES or Error: EACCES: permission denied.The message names an operation such as mkdir, open, rename, unlink or access.The failing path points to a global prefix, project directory, npm cache, home directory, or CI/container-mounted path.
Classify the denied path before changing permissions
Path is under a global npm prefix such as /usr/local or /usr →Prefer a Node version manager or a user-owned npm prefix instead of recurring privilege escalation.Path is inside your project →Find why project files are owned by another user or were created by an earlier privileged/container run.Path is under ~/.npm or another cache →Inspect cache ownership and the user/home environment that created it.Error appears only in Docker, CI or a mounted workspace →Compare the runtime UID/GID and mount ownership instead of fixing the host globally.The path or owner still does not make sense →Capture the exact EACCES line, process user, prefix and directory ownership before escalating.
Difficulty: Low–ModerateRisk: Low if permissions stay scoped
🔎 Quick Check — read the path, user and npm prefix

Do not start with sudo or chmod 777. First collect three facts.

whoami npm config get prefix npm root -g # Then inspect the directory named in the EACCES line: ls -ld /path/from/the/error

Verify: You can point to the exact denied path, say which user is running npm, and tell whether the path belongs to the global prefix, project, cache or another mounted location.

Did you classify the denied path?
ADSENSE · reserved slot after the first useful step

Why EACCES happens

npm ultimately asks the operating system to create, modify, remove or inspect files. On Unix-like systems, the kernel evaluates the process identity against directory and file permissions. npm reports EACCES when that operation is not permitted. The same code can therefore describe several different ownership mistakes. On Unix-like systems, accessing a path also requires search permission on each directory in the path; the final directory can look writable while an inaccessible parent blocks npm. Inspect the path components as well as the named target. On Linux, namei -l /path/from/the/error displays permissions and ownership along the path; on macOS, inspect each parent directory with ls -ld.

1
Operation
Was npm trying to mkdir, open, rename, unlink or access?
2
Path
Global prefix, project, cache, home directory, temp directory or mount?
3
Owner / runtime user
Who owns the parent directory, and which UID/user is npm actually running as?

A useful diagnosis explains all three. If a proposed fix does not explain why that particular user could not write that particular path, it is probably treating the symptom.

Fix #2 — global install prefix is not writable

This is the case covered directly by npm's official EACCES guidance: a global install such as npm install -g package fails because the global installation directory is not writable by the current user. npm recommends reinstalling Node/npm with a Node version manager as the preferred approach, or configuring npm to use a directory owned by your user.

Before changing anything, confirm that the denied path really belongs to the global prefix. A path under /usr/local/lib/node_modules, /usr/lib/node_modules or a similar system location often points here, but use npm config get prefix rather than assuming.

Keep the fix scoped to npm's installation model. Do not recursively take ownership of broad system directories just to make one package install. A version manager keeps Node and global packages in a user-controlled location and also makes Node-version changes easier to reproduce.

If a version manager is not appropriate, npm documents a user-owned prefix approach on macOS/Linux. Follow the current official instructions because shell startup files and paths differ by environment.

Verify: npm config get prefix points to the intended user-managed location, a normal global install succeeds without privilege escalation, and the installed command is available on PATH.

Fix #3 — the denied path is inside the project

If EACCES names node_modules, a generated file, build directory, lockfile or another path inside your project, changing the global npm prefix will not fix it. Inspect the project directory and its parent ownership. Common causes include a previous command that created files as another user, a container that wrote files with a different UID, an extracted archive with unexpected ownership, or a workspace copied from another environment.

pwd ls -ld . ls -ld node_modules 2>/dev/null ls -l package.json package-lock.json 2>/dev/null

Correct only the files or project tree that should actually belong to your development user. If the repository is shared, mounted, or managed by deployment tooling, confirm the intended owner first instead of changing it blindly.

Verify: the project files npm needs to modify are writable by the intended development/runtime user, a clean install no longer throws EACCES, and version-control status shows only expected dependency artifacts.

Fix #4 — npm cache or home-directory ownership is wrong

If the denied path is under your npm cache or home directory, inspect that exact directory. The important question is how files there became owned by another user. An old privileged npm run, container, restored backup or changed HOME environment can leave a user cache partially unwritable.

npm config get cache ls -ld "$(npm config get cache)" echo "$HOME"

Do not automatically delete the entire cache. First determine whether the problem is ownership, disk state, or a different HOME/cache location than expected. If ownership is wrong, repair only the directory that should belong to that user. If a CI job intentionally uses an ephemeral cache, fix the job's user/path policy rather than hand-editing the runner after every build.

Verify: npm reports the expected cache path, the intended process user can write there, and the same install command succeeds without changing unrelated system permissions.

Fix #5 — Docker, CI or bind-mounted workspace

Inside containers and CI, usernames can be misleading because file access is governed by numeric user and group IDs plus mount permissions. A host directory owned by one UID can be mounted into a container whose process runs as another UID. Similarly, one pipeline step may create root-owned artifacts that a later non-root step cannot replace.

id pwd ls -ld . ls -ld /path/from/the/error

Fix the ownership contract where it originates: the container user, volume/mount mapping, workspace initialization, or pipeline step. Avoid adding a blanket recursive permission change to every build if the real issue is that one stage writes as the wrong user.

Verify: a fresh runner/container reproduces the corrected UID/GID and mount policy, not just a manually repaired existing workspace.

What not to do

Do not run every npm command with sudo merely because one EACCES error appeared; classify the denied path first.Do not use chmod -R 777 on the project, home directory or system prefix. It removes useful access boundaries without explaining the ownership problem.Do not recursively chown broad system directories such as /usr or /usr/local to your user just to make one global package install.Do not delete package-lock.json for a permission error unless there is a separate lockfile reason.Do not assume every EACCES is a global-install problem. The denied path may be a project file, npm cache, temporary directory or mounted volume.Do not fix a CI/container error only by repairing one running workspace; make the user/mount policy reproducible on a fresh run.

Still seeing EACCES?

Capture the first EACCES line from a fresh run and keep the path intact. If a second run fails at a different location, do not combine them into one diagnosis until you know whether the first fix changed the filesystem state.

Before escalating, capture:the exact npm command and first EACCES line, including operation and pathNode and npm versionswhoami and, in containers/CI, idnpm config get prefix, npm root -g and npm config get cachels -ld for the denied path and its writable parentwhether the command is local, global, CI, Docker or a bind-mounted workspacethe last command/process that successfully created files in that directory

Official references

Still stuck? Ask the community

Share the exact EACCES line, denied path, operating system, Node/npm versions, whether the install is local/global, and the output of the ownership checks above.

Keep it safe: redact usernames or paths when they reveal sensitive organization details, and never post registry tokens, credentials, private package URLs or secrets from configuration files.
Powered by GitHub DiscussionsSign in with GitHub to comment. Reading comments does not require sign-in.

Loading community discussion…