Cách khắc phục lỗi 'Too many open files' (EMFILE) trên macOS

intermediate🍎 macOS2026-04-19| macOS (Monterey, Ventura, Sonoma), Node.js, MongoDB, Webpack, VS Code

Error Message

Error: EMFILE: too many open files
#macos#ulimit#node#mongodb

Thông báo lỗi

Bạn đang thực hiện một tiến trình build nặng hoặc khởi động cơ sở dữ liệu cục bộ thì bỗng nhiên mọi thứ dừng lại. Terminal của bạn hiển thị một loạt văn bản, thường kết thúc bằng dòng này:

Error: EMFILE: too many open files, open '/path/to/your/project/file.js'

Đôi khi, lỗi này còn khó hiểu hơn khi nó từ chối cho phép bạn thay đổi cài đặt theo cách thủ công:

ulimit: open files: cannot modify limit: Invalid argument

Nguyên nhân gốc rễ: Cơ chế bảo vệ của macOS

Mặc định, macOS khá "keo kiệt" với các file descriptor. Nó thường giới hạn một tiến trình duy nhất chỉ ở mức 256 hoặc 1.024 tệp. Mặc dù con số đó là đủ vào một thập kỷ trước, nhưng việc phát triển phần mềm hiện đại tiêu tốn nhiều tài nguyên hơn thế.

Hãy xem xét một dự án Next.js hoặc Vite tiêu chuẩn. Một lệnh npm install duy nhất có thể tải về hơn 30.000 tệp trong thư mục node_modules. Khi trình biên dịch của bạn cố gắng lập chỉ mục (index) các tệp này cùng một lúc, nó sẽ chạm tới giới hạn 1.024 đó gần như ngay lập tức. Hệ điều hành sẽ can thiệp, hủy yêu cầu và kích hoạt lỗi EMFILE để bảo vệ tài nguyên hệ thống.

Giải pháp 1: Khắc phục nhanh (Chỉ áp dụng cho Terminal hiện tại)

Nếu bạn chỉ cần hoàn tất một bản build ngay lúc này, bạn có thể tăng giới hạn cho tab terminal đang hoạt động. Thay đổi này chỉ mang tính tạm thời và sẽ biến mất ngay khi bạn đóng cửa sổ.

Trước tiên, hãy kiểm tra hạn chế hiện tại của bạn:

ulimit -n

Nếu kết quả trả về là 256 hoặc 1024, mức này quá thấp. Hãy chạy lệnh này để nâng giới hạn lên 65.536 cho phiên làm việc này:

ulimit -n 65536

Thử chạy lại bản build của bạn. Bây giờ nó sẽ có đủ không gian để hoàn tất mà không bị treo.

Giải pháp 2: Thiết lập vĩnh viễn cho Shell

Hầu hết người dùng macOS sử dụng Zsh. Để tránh phải chạy lệnh ulimit mỗi khi bắt đầu làm việc, hãy thêm nó vào tệp cấu hình shell của bạn.

  • Mở tệp cấu hình của bạn:
nano ~/.zshrc
  • Dán dòng này vào dưới cùng:
# Tăng giới hạn tệp mở tối đa cho phát triển
ulimit -n 65536
  • Lưu và thoát bằng cách nhấn Ctrl + O, Enter, sau đó là Ctrl + X.
  • Buộc các thay đổi có hiệu lực:
source ~/.zshrc

Giải pháp 3: Khắc phục trên toàn hệ thống (Khuyên dùng)

Các tinh chỉnh shell sẽ không có tác dụng nếu IDE của bạn (như VS Code) hoặc một dịch vụ chạy ngầm (như MongoDB) là nguyên nhân gây lỗi. Những ứng dụng này không phải lúc nào cũng kế thừa cài đặt từ .zshrc. Để khắc phục vấn đề này ở cấp độ kernel, bạn cần sử dụng launchctl.

Làm theo các bước sau để tạo cấu hình hệ thống vĩnh viễn:

  • Tạo một tệp cấu hình cấp hệ thống mới:
sudo nano /Library/LaunchDaemons/limit.maxfiles.plist
  • Chèn đoạn mã XML sau. Đoạn mã này thiết lập giới hạn "mềm" (soft limit) là 64k và giới hạn hệ thống "cứng" (hard limit) là 512k:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>limit.maxfiles</string>
  <key>ProgramArguments</key>
  <array>
    <string>launchctl</string>
    <string>limit</string>
    <string>maxfiles</string>
    <string>65536</string>
    <string>524288</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>ServiceIPC</key>
  <false/>
</dict>
</plist>
  • Bảo mật quyền hạn tệp để hệ thống chấp nhận:
sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
sudo chmod 644 /Library/LaunchDaemons/limit.maxfiles.plist
  • Tải các cài đặt mới:
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
  • Khởi động lại máy Mac của bạn. Việc khởi động lại hoàn toàn đảm bảo các thay đổi cấp kernel này được áp dụng cho mọi ứng dụng bạn mở.

Xác minh kết quả

Sau khi khởi động lại, hãy xác minh rằng các giới hạn mới đã hoạt động. Mở một terminal mới và chạy:

launchctl limit maxfiles

Bạn sẽ thấy 65536524288 trong kết quả trả về. Nếu các con số vẫn thấp, hãy kiểm tra lại cú pháp XML trong tệp .plist của bạn.

Phòng ngừa và các phương pháp hay nhất

  • Quản lý Watchers: Các công cụ như nodemon hoặc chokidar sử dụng trình theo dõi tệp (file watchers) để phát hiện thay đổi. Nếu bạn để năm dự án khác nhau cùng chạy, chúng sẽ nhanh chóng làm cạn kiệt hạn ngạch của bạn. Hãy đóng những gì bạn không sử dụng.
  • Kiểm tra node_modules: Đảm bảo IDE của bạn không cố gắng lập chỉ mục toàn bộ thư mục node_modules để tìm kiếm kết quả. Hầu hết các trình soạn thảo hiện đại đều loại trừ thư mục này theo mặc định, nhưng bạn vẫn nên kiểm tra lại cài đặt của mình.
  • Dọn dẹp các kết nối: Nếu bạn đang viết các tập lệnh tùy chỉnh, hãy luôn đóng các luồng dữ liệu tệp (file streams) và kết nối cơ sở dữ liệu. Ngay cả giới hạn 65k cũng không thể cứu bạn khỏi tình trạng rò rỉ bộ nhớ (memory leak) khiến các descriptor luôn ở trạng thái mở vô thời hạn.

Related Error Notes