TL;DR โ Quick Fix
This warning fires when more than 10 listeners are attached to the same event on a single EventEmitter. Node.js treats 10 as the default safety cap to catch accidental leaks.
What to do depends on why you have that many listeners:
- You intentionally need more listeners โ raise the limit with
emitter.setMaxListeners(n) - You're adding listeners inside a loop or on each request โ you have a real leak โ fix the code, don't just raise the limit
What Triggers This Warning
The exact message you'll see:
(node:1234) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit
Node.js logs this when a single emitter has more than defaultMaxListeners (default: 10) listeners registered for the same event. Common culprits:
- Registering a listener on every HTTP request without ever removing it
- Calling
emitter.on()inside a function that runs repeatedly โ say, a middleware that fires on every route - Using a library (
readline,socket.io,chokidar) that registers listeners internally, while you create a fresh instance on each request - Forgetting to call
removeListener()oroff()during cleanup
Step 1 โ Find Which Emitter Is Leaking
Run your app and read the stack trace. Node.js 10+ prints it automatically:
(node:1234) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 listeners added to [EventEmitter].
at EventEmitter.addListener (node:events:596:17)
at Server.<anonymous> (/app/server.js:42:10) <-- your file
...
That file + line number is where the listener is being added. Open it and check whether it sits inside a callback, loop, or request handler.
Not sure how many listeners you have right now? Print it:
console.log(emitter.listenerCount('data')); // how many 'data' listeners?
Step 2 โ Fix the Real Leak (Most Cases)
Most of the time, the problem is simple: you're attaching a listener inside a function that runs over and over, and nothing ever removes it. Move the registration outside that function โ or remove the listener once you're done with it.
Bad pattern โ listener added on every call
// server.js
app.get('/stream', (req, res) => {
// BUG: adds a new listener on every request, never removed
process.on('exit', () => res.end());
});
After 11 requests, the warning fires. After thousands, you have a memory leak.
Fixed โ remove listener when done
app.get('/stream', (req, res) => {
const cleanup = () => res.end();
process.once('exit', cleanup); // 'once' removes itself automatically
res.on('close', () => {
process.removeListener('exit', cleanup); // explicit cleanup if client disconnects first
});
});
Use once() instead of on() for one-time events
// on() keeps the listener alive forever
emitter.on('finish', handleFinish);
// once() removes itself after firing โ no manual cleanup needed
emitter.once('finish', handleFinish);
This single change fixes the majority of listener leak reports in Express and Koa apps.
Creating new instances per request (common with readline, socket.io)
// BAD: new readline interface on every request
app.post('/process', (req, res) => {
const rl = readline.createInterface({ input: req });
rl.on('line', processLine);
// rl is never closed โ listeners pile up with each request
});
// GOOD: close when done
app.post('/process', (req, res) => {
const rl = readline.createInterface({ input: req });
rl.on('line', processLine);
rl.on('close', () => res.json({ ok: true }));
req.on('end', () => rl.close()); // guarantee cleanup
});
Step 3 โ Raise the Limit Only When You Actually Need More Listeners
Sometimes the listeners are all legitimate โ a pub/sub channel with 20 subscribers, or a shared event bus in a plugin system. In those cases, raise the limit on that specific emitter:
const EventEmitter = require('events');
const emitter = new EventEmitter();
// Allow up to 30 listeners on this emitter
emitter.setMaxListeners(30);
emitter.on('message', handler1);
emitter.on('message', handler2);
// ... up to 30 safely
There's also a global option โ but use it carefully:
const EventEmitter = require('events');
EventEmitter.defaultMaxListeners = 20;
Warning: raising the global default hides real leaks anywhere else in the same process. Stick to per-emitter setMaxListeners() whenever possible.
Step 4 โ Silence the Warning for Known-Safe Third-Party Code
Some libraries โ AWS SDK, certain socket.io internals โ trigger the warning even when they clean up correctly. You can suppress it on a specific emitter without touching the limit logic:
// 0 = unlimited listeners, warning suppressed
emitter.setMaxListeners(0);
Only reach for this when you're certain about what's registering listeners and why. It's a mute button, not a fix.
Verification โ Confirm the Fix Worked
- Restart your app and watch the console โ the warning should be gone.
- Replay the code path that triggered it: simulate load, fire several requests, stress-test the route.
- Add a sanity check during development:
setInterval(() => {
const count = emitter.listenerCount('data');
if (count > 5) console.warn(`High listener count on 'data': ${count}`);
}, 5000);
- In production, catch the warning programmatically and ship it to your monitoring stack:
process.on('warning', (warning) => {
if (warning.name === 'MaxListenersExceededWarning') {
console.error('Listener leak detected:', warning.message, warning.stack);
// forward to Datadog, Sentry, etc.
}
});
Summary
- The warning means 11+ listeners are registered to one event on one emitter
- 99% of the time it's a real bug โ a listener added inside a loop or per-request handler with no cleanup
- Reach for
once()on one-shot events,removeListener()/off()for explicit cleanup, and always close streams and interfaces when you're done - Only call
setMaxListeners(n)when you genuinely need many listeners on purpose โ not to make the warning disappear

