MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [Socket]. MaxListeners is 10. Use emitter.setMaxListeners() to increase limit
Node.js · updated 2026-11-04
Printed by Node's EventEmitter when an 11th listener for one event is added to the same emitter (the default limit is 10). It is a warning, not a crash, but it usually means code calls .on() inside something that runs repeatedly, so listeners and memory grow until the process restarts. AbortSignal has the same check, worded "Possible EventTarget memory leak detected".
(node:48213) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [Socket]. MaxListeners is 10. Use emitter.setMaxListeners() to increase limit (Use `node --trace-warnings ...` to show where the warning was created)
Quick fix
Run with node --trace-warnings (or listen for process.on('warning')) to get the stack of the .on() call that crossed the limit. Move that listener out of the per-request or per-retry path, use .once() if it should fire one time, or remove it with .off() when the work is done. Raise setMaxListeners only when many listeners on one emitter are expected.
- Start the process with --trace-warnings (NODE_OPTIONS works too) to see where the listener is added.
- Check whether that .on() runs per request, per retry or in a loop. Register it once at startup instead.
- Use .once() for one-shot events, or call .off()/removeListener when the operation finishes.
- For AbortSignal, create a new AbortController per operation instead of reusing one signal for many requests.
- Only if many listeners are legitimate (for example one per worker), call emitter.setMaxListeners(n) on that emitter.
// Before: a new 'error' listener on the shared socket for every request
// app.get('/report', (req, res) => { socket.on('error', handle); ... });
// After: registered once
socket.on('error', handle);
// Or scoped to one operation and removed afterwards
function send(payload: Buffer) {
const onError = (err: Error) => logger.warn('send failed', { message: err.message });
socket.once('error', onError);
socket.write(payload, () => socket.off('error', onError));
}
// Log the stack of every process warning (helps in production)
process.on('warning', (w) => logger.warn(w.name, { message: w.message, stack: w.stack }));Why it happens
Listener added per request
A handler calls .on() on a long-lived object (a socket, database client, process, a shared stream) every time it runs, and never removes it.
Retries that re-subscribe
Reconnect or retry code registers its error and close handlers again on each attempt.
One AbortSignal for many requests
Passing the same signal to many fetch calls adds an 'abort' listener to it each time; the 11th triggers the EventTarget version of this warning.
Many legitimate listeners
Some designs attach more than 10 listeners on purpose, for example one per worker or plugin. Then the warning is noise and the limit can be raised for that emitter.
Other fixes
- emitter.listenerCount(event) in a debug log shows whether the count keeps growing between requests.
- events.setMaxListeners(n, signal) raises the limit for an AbortSignal or other EventTarget.
Same error, other wording
- MaxListenersExceededWarning: Possible EventTarget memory leak detected. 11 abort listeners added to [AbortSignal]. MaxListeners is 10. Use events.setMaxListeners() to increase limit
- MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added to [process].
- MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 close listeners added to [Client].
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 MaxListenersExceededWarning in production, find where that listener is added (run with --trace-warnings if the stack is missing), and change it so the listener is added once or removed when the work is done.
Related errors
- FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
- Error: EMFILE: too many open files, open '/app/uploads/0412.jpg'
- [UnhandledPromiseRejection: This error originated either by throwing inside of an async...
More in Memory, async and the process
- RangeError: Maximum call stack size exceededNode.js
- RecursionError: maximum recursion depth exceededPython
- RuntimeError: Event loop is closedPython
- RuntimeWarning: coroutine 'fetch_user' was never awaitedPython
- Error: listen EADDRINUSE: address already in use :::3000Node.js
- OSError: [Errno 98] Address already in usePython
- Error: error:0308010C:digital envelope routines::unsupportedNode.js
- Error: ENOENT: no such file or directory, open '/app/config.json'Node.js
- Error: EACCES: permission denied, open '/app/data/cache.json'Node.js
References: Node.js docs: events (defaultMaxListeners, setMaxListeners), Node.js source: the warning in lib/events.js, Node.js docs: --trace-warnings