← Debugging Guides

阅读中文版本

Node.js EMFILE: too many open files

Fix the Node.js EMFILE error when the process exhausts file descriptors through unclosed streams, watchers, or connection pools.

intermediatedebuggingnodefilesystemresource-leak

Symptoms

  • Error: EMFILE: too many open files, open '/path'
  • fs.watch or chokidar stops emitting after a while
  • Server cannot accept new sockets under sustained load

Possible causes

  • File streams opened in a loop are never closed
  • Watchers or sockets accumulate because listeners are not destroyed
  • A connection pool size exceeds the OS file-descriptor limit
  • Recursive directory reads open thousands of files concurrently

Diagnosis steps

Count descriptors used by the process

ls /proc/<pid>/fd | wc -l

Interpretation

A number close to ulimit indicates descriptor exhaustion; growth over time points to a leak.

Check the per-process limit

ulimit -n

Interpretation

The limit may be a low default (1024). Raising it buys time but does not fix an unclosed handle.

Inspect open handles via inspector or lsof

lsof -p <pid> | wc -l

Interpretation

Repeated paths show the leak source so you can close streams at the right place.

Root cause

Every open file, socket, and watcher consumes one file descriptor. Node does not close handles for you, so code that opens resources in loops or on every request gradually exhausts the OS limit, after which any open call fails with EMFILE.

Fix

Close every stream in a finally block, destroy watchers and sockets when done, and bound concurrency when reading many files. If a higher limit is genuinely needed, raise ulimit -n and the systemd LimitNOFILE setting, and cap pool sizes accordingly.

Verify the fix

Watch the descriptor count with ls /proc/<pid>/fd | wc -l while exercising the load path; the count should stay flat instead of climbing, and EMFILE should not recur.

Symptoms

Operations begin failing with EMFILE: too many open files after the process has run for a while, or immediately if thousands of resources are opened concurrently. Restarting the process clears it temporarily.

Diagnosis

Count the descriptors in use and compare with the limit:

ls /proc/$(pgrep -f 'node server.js')/fd | wc -l
ulimit -n

Then use lsof -p <pid> to see which paths dominate. A recurring path reveals the leak.

Fix

Close resources deterministically and cap concurrency:

const handle = await fs.promises.open(p, 'r');
try {
  // ...read...
} finally {
  await handle.close();
}

For bulk reads, batch with p-limit instead of launching everything at once, and destroy watchers on shutdown.

Verification

Re-apply load and re-check the descriptor count. It should plateau rather than grow, and no new EMFILE errors should appear.