Fix 'MaxListenersExceededWarning: Possible EventEmitter Memory Leak Detected' in Node.js

intermediate๐Ÿ’š Node.js2026-04-15| Node.js (all versions), Linux / macOS / Windows โ€” any project using EventEmitter, streams, HTTP servers, or libraries like socket.io, fs.watch, readline

Error Message

(node:1234) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit
#Node.js#EventEmitter#Memory Leak#Events

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() or off() 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

Related Error Notes