Fix 'npm ERR! code EACCES' khi cài đặt global packages trên macOS

beginner🍎 macOS2026-07-04| macOS (Monterey / Ventura / Sonoma), Node.js cài qua bộ cài chính thức hoặc Homebrew, npm 6+

Error Message

npm ERR! code EACCES npm ERR! syscall access npm ERR! path /usr/local/lib/node_modules
#npm#nodejs#permission#macOS

Tình huống

Bạn đang cài đặt Mac mới hoặc bắt đầu tham gia một dự án, chạy lệnh npm install -g thông thường và bị chặn ngay bởi lỗi này:

npm ERR! code EACCES
npm ERR! syscall access
npm ERR! path /usr/local/lib/node_modules
npm ERR! errno -13
npm ERR!
npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
npm ERR!  [Error: EACCES: permission denied, access '/usr/local/lib/node_modules'] {
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/usr/local/lib/node_modules'
npm ERR! }
npm ERR! The operation was rejected by your operating system.

Cách sửa mà hầu hết các câu trả lời trên Stack Overflow gợi ý — thêm sudo vào trước lệnh — về mặt kỹ thuật là có tác dụng, nhưng về lâu dài sẽ tạo ra mớ hỗn độn còn tệ hơn. Các package global được cài với quyền root có thể xung đột với môi trường user của bạn và gây ra nhiều vấn đề phân quyền hơn sau này.

Có ba cách sửa đúng đắn. Hãy chọn cách phù hợp với thiết lập của bạn.

Tại sao lỗi này xảy ra

Khi Node.js được cài đặt qua bộ cài .pkg chính thức hoặc qua một số cách cài Homebrew, nó đặt các module global vào /usr/local/lib/node_modules. Trên macOS (đặc biệt từ Catalina trở đi với SIP được bật), tài khoản user của bạn mặc định không có quyền ghi vào thư mục đó. Vì vậy, ngay khi npm cố ghi vào đó, hệ điều hành sẽ chặn lại.

Kiểm tra xem ai là chủ sở hữu thư mục:

ls -la /usr/local/lib/ | grep node_modules

Nếu bạn thấy root là chủ sở hữu, đó chính là vấn đề.

Cách sửa 1: Chuyển sang dùng nvm (giải pháp lâu dài)

Đây là cách tiếp cận đúng đắn về lâu dài. nvm (Node Version Manager) cài đặt Node hoàn toàn bên trong thư mục home của bạn, nên npm không bao giờ cần quyền root cho bất kỳ thao tác nào.

# Cài đặt nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# Tải lại cấu hình shell
source ~/.zshrc   # hoặc ~/.bashrc nếu bạn dùng bash

# Cài phiên bản Node bạn cần
nvm install --lts
nvm use --lts

# Xác nhận đang dùng node do nvm quản lý
which node
# Kết quả sẽ tương tự: /Users/yourname/.nvm/versions/node/v20.x.x/bin/node

Sau khi chuyển sang nvm, các lệnh cài global hoạt động bình thường mà không gặp vấn đề phân quyền nào:

npm install -g typescript
npm install -g pnpm

Một điều cần lưu ý: nếu trước đó bạn đã cài Node theo phạm vi toàn hệ thống, hãy đảm bảo PATH của bạn ưu tiên phiên bản nvm. Chạy which nodenode --version để xác nhận.

Cách sửa 2: Đổi thư mục global prefix của npm sang thư mục user

Nếu bạn cần giữ nguyên cài đặt Node hiện tại (hoặc đang dùng máy của công ty mà không thể thay đổi cách cài Node), hãy chuyển hướng thư mục package global của npm sang nơi user của bạn có quyền sở hữu.

# Tạo thư mục cho các package npm global
mkdir -p ~/.npm-global

# Báo cho npm biết dùng thư mục này
npm config set prefix '~/.npm-global'

# Thêm vào PATH — thêm dòng này vào ~/.zshrc hoặc ~/.bash_profile
export PATH=~/.npm-global/bin:$PATH

# Tải lại cấu hình
source ~/.zshrc

Kiểm tra xem cấu hình đã có hiệu lực chưa:

npm config get prefix
# Kết quả sẽ là: /Users/yourname/.npm-global

Bây giờ thử cài một package global:

npm install -g nodemon
nodemon --version

Cách sửa 3: Thay đổi quyền sở hữu thư mục hiện tại

Chỉ dùng cách này nếu bạn là người duy nhất sử dụng máy và không muốn thay đổi cách cấu hình Node hay npm. Bạn sẽ chuyển quyền sở hữu /usr/local/lib/node_modules về cho chính mình.

# Kiểm tra tên user của bạn
whoami

# Thay đổi quyền sở hữu (thay 'yourname' bằng tên user thực của bạn)
sudo chown -R yourname /usr/local/lib/node_modules
sudo chown -R yourname /usr/local/bin

Hãy cẩn thận với cách này trên các máy dùng chung — bạn đang cho phép một tài khoản user ghi vào các thư mục hệ thống. Trên Mac dùng cá nhân thì ổn, nhưng hãy hiểu rõ sự đánh đổi này.

Kiểm tra lại

Sau khi áp dụng bất kỳ cách sửa nào, hãy xác nhận mọi thứ hoạt động bình thường:

# Lệnh này phải hoàn thành mà không có lỗi EACCES
npm install -g eslint

# Phải hiển thị số phiên bản
eslint --version

# Kiểm tra xem các package global hiện được cài ở đâu
npm root -g
npm bin -g

Nếu npm bin -g trỏ đến một đường dẫn nằm trong $PATH của bạn, là ổn rồi.

Mẹo: Hiểu về phân quyền liên quan

Nếu bạn đang xử lý vấn đề phân quyền trên nhiều thư mục khác nhau hoặc muốn hiểu cần đặt quyền như thế nào, Unix Permissions Calculator trên ToolCraft rất hữu ích để tính toán giá trị chmod một cách trực quan. Tôi thường dùng nó khi cần nhanh chóng xác định các bit phân quyền mà không muốn phải đổi số octal trong đầu.

Kinh nghiệm thực tế

  • Đừng bao giờ dùng sudo npm install -g như một cách sửa thông thường. Nó chỉ chạy được một lần nhưng tạo ra các file thuộc sở hữu root, khiến các lần cài sau đó không có sudo sẽ bị lỗi, tạo thành vòng lặp không hồi kết.
  • nvm là lựa chọn mặc định cho máy cá nhân. Nó còn cho phép bạn chuyển đổi phiên bản Node theo từng dự án bằng file .nvmrc, điều này đã là lý do đủ để dùng.
  • Cách đổi prefix (Cách sửa 2) rất phù hợp khi làm việc trên máy công ty mà bạn không kiểm soát được cách cài Node.
  • Nếu bạn cài Mac mới và clone một repo dotfiles có sẵn, hãy đảm bảo cấu hình PATH trong .zshrc được áp dụng trước PATH của hệ thống — nếu không, các bản cài từ nvm sẽ bị che bởi Node của hệ thống.

Related Error Notes