How to fix npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib/node_modules' in npm

npmBEGINNERMEDIUMUpdated Dec 2, 2025· 169 views

npm tried to change ownership of a file or directory but the OS/FS blocks lchown (common on Windows/WSL and non-root global prefixes). Use user-owned prefixes and avoid chown on unsupported filesystems.

Quick fix

Fixes npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib/node_modules'

npm config get prefix
stat -f -c %T $(npm config get prefix) 2>/dev/null || stat -f $(npm config get prefix)
  1. Check prefix and filesystem support
  2. Use user-owned prefix instead of chown
  3. Avoid chown on Windows/WSL; reinstall locally
  4. Clean and retry
npm — error
npm config get prefix
stat -f -c %T $(npm config get prefix) 2>/dev/null || stat -f $(npm config get prefix)
npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib/node_modules'

What this error means

npm occasionally calls lchown when installing globally or adjusting permissions in node_modules. On Windows/WSL and many container mounts, lchown is either unsupported or restricted, so the syscall returns EPERM and the install stops. This happens when installing globally as a non-root user, when the prefix is root-owned, inside Docker/CI with mismatched users, or on NTFS/SMB where chown semantics are limited. The fix is usually to avoid needing chown by keeping npm files in user-owned locations.

Typical symptoms

  • npm install -g fails with EPERM: lchown on /usr/local/lib/node_modules
  • Works with sudo/Admin but fails as normal user
  • WSL/Windows emits "operation not permitted" even with correct mode bits
  • Docker/CI jobs fail when running npm as non-root against root-owned prefixes
  • Staging directories remain after failure

Common causes

  1. Global prefix owned by root/system; npm attempts ownership adjustments
  2. Filesystem does not support chown (NTFS/SMB, some container volumes)
  3. Docker/CI running as non-owner of the mounted prefix
  4. Corporate policies blocking chown on /usr/local or /opt/homebrew
  5. Mixing sudo and user installs leaving root-owned artifacts

How to fix "npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib/node_modules'"

1Check prefix and filesystem support
bash
npm config get prefix
stat -f -c %T $(npm config get prefix) 2>/dev/null || stat -f $(npm config get prefix)
2Use user-owned prefix instead of chown
bash
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
npm config set cache ~/.npm-cache
export PATH="$HOME/.npm-global/bin:$PATH"
3Avoid chown on Windows/WSL; reinstall locally

On NTFS/SMB mounts, chown is ignored or blocked. Install on a Linux/ext4 path (WSL ~/projects) or run without forcing ownership changes.

4Clean and retry
bash
sudo rm -rf $(npm config get prefix)/lib/node_modules/.staging 2>/dev/null || true
npm cache clean --force
npm install -g <package>
5Align users in Docker/CI

Run containers with matching UID/GID or chown the workspace/prefix once at startup so npm never needs to call lchown.

Advanced notes

Homebrew installs under /opt/homebrew on macOS can drift to root-owned; prefer nvm/fnm to keep globals in $HOME and avoid chown. In CI, ensure the workspace is owned by the job user before npm ci; copying caches from root-owned layers often triggers lchown failures. On Windows/WSL, chown is largely a no-op—using per-user prefixes and avoiding sudo eliminates the syscall entirely.

Frequently asked questions

What does "npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib" mean?
npm occasionally calls lchown when installing globally or adjusting permissions in node_modules. On Windows/WSL and many container mounts, lchown is either unsupported or restricted, so the syscall returns EPERM and the install stops. This happens when installing globally as a non-root user, when the prefix is root-owned, inside Docker/CI with mismatched users, or on NTFS/SMB where chown semantics are limited.
How do I fix "npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib"?
npm tried to change ownership of a file or directory but the OS/FS blocks lchown (common on Windows/WSL and non-root global prefixes). Use user-owned prefixes and avoid chown on unsupported filesystems.
What causes "npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib"?
Global prefix owned by root/system; npm attempts ownership adjustments Filesystem does not support chown (NTFS/SMB, some container volumes) Docker/CI running as non-owner of the mounted prefix
Is the "npm ERR! Error: EPERM: operation not permitted, lchown '/usr/local/lib" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading