Skip to content

Node.js errorNode.js: “Error: EMFILE: too many open files”

A script, test runner or dev server fails with Error: EMFILE: too many open files, open '...' or EMFILE: too many open files, watch. Every open file, socket and file watcher uses a file descriptor, and the process hit its per-process limit. It shows up most on macOS, where the default limit per process is low.

Short answer

If the message ends in watch, the file watcher is the problem: install Watchman (brew install watchman) for Jest and React Native, or exclude node_modules from the watcher. If it ends in open, your code opens too many files at once: limit how many run in parallel and make sure every file handle and stream is closed. Raising the limit with ulimit -n helps as a stopgap but does not fix a leak.

What is using up file descriptors, and the fix

  1. 01

    Thousands of files opened in parallel

    “Maximum number of file descriptors allowable on the system has been reached, and requests for another descriptor cannot be fulfilled until at least one has been closed.” Node.js, Errors: common system errors

    Promise.all over a large list of fs.readFile calls opens them all at once. Process them in small batches instead.

    for (let i = 0; i < files.length; i += 50) {
      await Promise.all(files.slice(i, i + 50).map((f) => fs.promises.readFile(f)));
    }
  2. 02

    File watchers (EMFILE ... watch)

    Jest in watch mode, Metro for React Native and some dev servers watch every file in the project. Without Watchman, the Node.js fallback watcher can open a descriptor for every watched file or folder, depending on the platform. Install Watchman, and keep node_modules and build folders out of the watch list.

    brew install watchman
  3. 03

    File handles that are never closed

    “It is the application's responsibility to close it and make sure there's no file descriptor leak.” Node.js, fs: file handles and streams

    Opening files with fs.promises.open and never closing them, or streams that are never consumed or destroyed, leak descriptors until the limit is hit, usually after the app has run for a while. Close handles in a finally block.

    const fh = await fs.promises.open(file);
    try {
      /* read */
    } finally {
      await fh.close();
    }
  4. 04

    A low limit for the process

    “To remedy a low limit, run ulimit -n 2048 in the same shell that will run the Node.js process.” Node.js, Errors: common system errors

    Check the current limit and raise it for the shell that starts Node.js. This is a stopgap for a real workload, not a fix for a leak.

    ulimit -n          # current limit
    ulimit -n 2048     # raise it for this shell

Checked against the vendor’s docs on : Node.js: Errors (common system errors); Node.js: File system.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: Node.js “ENOENT: no such file or directory” · Node.js “JavaScript heap out of memory” · npm “EACCES: permission denied” · Vercel ERR_TOO_MANY_REDIRECTS

Questions

More on this error

Can a server get EMFILE from network connections?

Yes. Sockets use file descriptors too, so a server holding many connections, or a client that never closes its HTTP connections, can hit the same limit. Close connections and reuse them with keep-alive.

Is graceful-fs a fix?

It queues and retries file opens after EMFILE, which hides bursts of parallel opens. It does not fix handles that are never closed, so find the leak first.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.