EMFILE / ENFILE: too many open files (watchpack, chokidar, ulimit)
A bundler or Node process crashed with EMFILE / ENFILE — too many open files. The OS hit a per-process or system-wide file-descriptor limit, almost always during file watching or a huge parallel read.
What EMFILE and ENFILE mean
Error: EMFILE: too many open files (libuv / Node errno -24) means the process reached its nofile soft limit. ENFILE is rarer: the kernel’s global file table is full. Dev servers (webpack, Vite, Next, Metro, watchpack, chokidar) open a handle per watched file — monorepos blow past macOS’s historic 256/1024 defaults quickly.
Error: EMFILE: too many open files, watch
at FSWatcher._handle.onchange (node:internal/fs/watchers:…)
# or from watchpack / chokidar during next dev / vite
Cause table
| Symptom | Likely cause | Fix |
|---|---|---|
EMFILE on watch / chokidar | File watcher + low ulimit -n | Raise nofile; narrow watch globs; exclude node_modules / .git |
| Only on macOS | Low default soft limit | ulimit -n 10240; persist via launchctl limit maxfiles / shell profile |
| Only under systemd service | Default LimitNOFILE (often 1024) | LimitNOFILE=65535 in the unit; systemctl daemon-reload |
| Only in Docker / K8s | Container nofile / pid limits | --ulimit nofile=65535:65535; pod securityContext / container ulimits |
| ENFILE | System-wide table full | Find runaway processes (lsof); reboot last resort; raise fs.file-max |
| Linux: “OS file watch limit reached” | inotify watches exhausted | Raise fs.inotify.max_user_watches (and often max_user_instances) |
Raise the limit (quick → lasting)
# Current soft/hard limits
ulimit -n
ulimit -Hn
# This shell only
ulimit -n 10240
# Linux persistent (example): /etc/security/limits.conf
# * soft nofile 65535
# * hard nofile 65535
# systemd service snippet
# [Service]
# LimitNOFILE=65535
# Docker
docker run --ulimit nofile=65535:65535 …
On modern Linux, pam limits apply to login shells; services started by systemd ignore limits.conf unless LimitNOFILE is set. GUI/IDE terminals sometimes inherit a different limit than SSH.
Watchers: webpack, Vite, Next
- Exclude heavy trees:
watchOptions.ignored/ Viteserver.watch.ignoredfor**/node_modules/**, build output, caches. - Prefer polling only when you must (Docker Desktop file sharing):
CHOKIDAR_USEPOLLING=1orwatchOptions.usePolling— slower, fewer native watches. - Monorepo tip: watch only the packages you edit; don’t watch the entire repo root.
- Next.js / large apps: ensure
node_modulesisn’t being watched twice via nested projects.
Linux inotify
cat /proc/sys/fs/inotify/max_user_watches
# temporary
sudo sysctl -w fs.inotify.max_user_watches=524288
# persistent: fs.inotify.max_user_watches=524288 in /etc/sysctl.d/*.conf
inotify limits are separate from ulimit -n. You can have free file descriptors and still fail to add watches.
Who is holding files open?
lsof -p $(pgrep -n node) | wc -l
lsof +D /path/to/project 2>/dev/null | wc -l
# per-process count (Linux)
ls /proc/PID/fd | wc -l
A stuck previous node / IDE language server can leak descriptors across restarts — kill orphans before raising limits forever. Mystery errno strings? Paste into What is this?.
Related
JavaScript heap out of memory · Killed: exit code 137 · EADDRINUSE · ENOSPC no space left · npm ERR! code ELIFECYCLE
Questions
What is Error EMFILE: too many open files?
The process hit its open file-descriptor (nofile) limit. Dev file watchers and large parallel I/O are the usual triggers.
EMFILE vs ENFILE?
EMFILE is per-process. ENFILE is system-wide. Fix EMFILE with ulimit/LimitNOFILE; for ENFILE find runaway processes and consider fs.file-max.
Why does Vite / webpack / Next hit this?
They watch thousands of source files. macOS and systemd defaults are often too low for monorepos.
Is ulimit -n enough under systemd?
No. Set LimitNOFILE in the unit file; login-shell ulimit does not apply to most services.
What about inotify max_user_watches?
On Linux, watchers also need enough inotify watches — raise fs.inotify.max_user_watches separately from ulimit.
How do I see how many files a process has open?
lsof -p PID | wc -l, or ls /proc/PID/fd | wc -l on Linux.
Published .