Error: EMFILE: too many open files, open '/app/uploads/0412.jpg'
Node.js · updated 2026-11-03
Thrown by fs and net calls when the process already holds as many file descriptors as the operating system allows (ulimit -n, often 256 on macOS and 1024 on Linux). Sockets and file watchers count too, not just files.
Error: EMFILE: too many open files, open '/app/uploads/0412.jpg'
at async open (node:internal/fs/promises:639:25)
at async Object.readFile (node:internal/fs/promises:1242:14)
at async Promise.all (index 3871)
at async processUploads (file:///app/src/jobs/thumbnails.js:22:3) {
errno: -24,
code: 'EMFILE',
syscall: 'open',
path: '/app/uploads/0412.jpg'
}Quick fix
Look for code that opens many files or connections at once, usually Promise.all over a big list. Process them in batches or with a concurrency limit, make sure every stream and handle is closed, and only then raise the limit (ulimit -n or LimitNOFILE) if the workload truly needs more.
- Check the current limit with ulimit -n, and count open descriptors with lsof -p <pid> | wc -l.
- Replace Promise.all over thousands of items with batches or a concurrency limit (p-limit, or a simple queue).
- Close what you open: use fs.promises with await, call stream.destroy() on errors, and close database and HTTP clients you create per request.
- In development, a file watcher on a big folder (node_modules) can hit the limit; exclude it or use polling.
- Raise the limit for the service once the leak is ruled out: LimitNOFILE in systemd, --ulimit in Docker.
import pLimit from 'p-limit';
import { readFile } from 'node:fs/promises';
const limit = pLimit(50); // at most 50 files open at once
// Before: await Promise.all(paths.map((p) => readFile(p)));
const buffers = await Promise.all(paths.map((p) => limit(() => readFile(p))));Why it happens
Unbounded parallel work
Promise.all over thousands of files, images or URLs opens all of them at once and runs past the limit partway through the list.
Leaked handles
Streams that error without being destroyed, files opened with fs.open and never closed, or HTTP agents and database clients created per request keep descriptors open until the process runs out.
File watchers in development
Watchers on large trees (node_modules, build output) use one descriptor per watched path on some systems and hit low macOS limits quickly.
A low default limit
macOS defaults to 256 per shell and many Linux services to 1024, which a busy server with many sockets can legitimately exceed.
Other fixes
- graceful-fs queues open calls when it sees EMFILE instead of failing; useful for tools, not a fix for leaks.
- Reuse one HTTP agent with keepAlive instead of creating clients per request.
Same error, other wording
- Error: EMFILE: too many open files, watch '/app/node_modules'
- Error: connect EMFILE 10.0.0.5:5432 - Local (undefined:undefined)
- Error: ENFILE: file table overflow
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the EMFILE errors in production, show me the code paths that were opening files or sockets when they hit the limit, and add a concurrency limit or close the leaked handles before raising any limits.
Related errors
- FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
- Error: ENOENT: no such file or directory, open '/app/config.json'
- Error: listen EADDRINUSE: address already in use :::3000
References: Node.js errors: common system errors (EMFILE), systemd docs: LimitNOFILE