Introduction
Node.js EventEmitters warn when more than 10 listeners are attached to a single event. This default limit catches a common memory leak pattern where listeners are added repeatedly (e.g., inside a loop or on every request) without being removed, causing the emitter to hold references that prevent garbage collection.
This warning often indicates a design issue where event listeners are being registered in the wrong scope or lifecycle.
Symptoms
- Console shows "(node:1234) MaxListenersExceededWarning: Possible EventEmitter memory leak detected"
- "11 listeners added to [emitter]. Use emitter.setMaxListeners() to increase limit"
- Memory usage grows over time as listeners accumulate
Common Causes
- Adding event listener inside a loop or request handler without removing it
- Re-subscribing to events on reconnection without cleaning up old listeners
- Global or singleton EventEmitter accumulating listeners from multiple modules
Step-by-Step Fix
- 1.Remove listeners when they are no longer needed: Clean up after yourself.
- 2.```javascript
- 3.const EventEmitter = require('events');
- 4.const emitter = new EventEmitter();
// BAD: listener added every time, never removed function handleRequest(req) { emitter.on('data', (data) => { res.write(data); // Accumulates on every request }); }
// GOOD: listener removed after use function handleRequest(req) { const handler = (data) => res.write(data); emitter.on('data', handler); res.on('end', () => { emitter.off('data', handler); }); } ```
- 1.Use once() for single-fire events: Automatically removes the listener after firing.
- 2.```javascript
- 3.const EventEmitter = require('events');
- 4.const emitter = new EventEmitter();
// BAD: emitter.on('ready', () => { console.log('Ready!'); // This listener stays forever even though it fired once });
// GOOD: emitter.once('ready', () => { console.log('Ready!'); // Listener automatically removed after first fire }); ```
- 1.Find and fix the leaking listener: Use emitter.listeners() to debug.
- 2.```javascript
- 3.const EventEmitter = require('events');
// Track listener additions:
const emitter = new EventEmitter();
emitter.on('newListener', (event, listener) => {
const count = emitter.listenerCount(event);
if (count > 5) {
console.warn(Warning: ${count} listeners on event '${event}');
console.trace('Listener added from:');
}
});
// List all listeners for an event: console.log(emitter.listeners('data')); ```
Prevention
- Use once() instead of on() for events that only need to fire once
- Always pair listener addition with removal in cleanup code
- Monitor emitter.listenerCount() in production for key events
- Consider using AbortController signals for automatic listener cleanup