How to fix Error: Unhandled error event (no error handler) in Node.js

Node.jsINTERMEDIATEHIGHUpdated Dec 22, 2025· 167 views

This error occurs when an EventEmitter instance emits an "error" event but no listener is registered to handle it. Node.js will throw an unhandled error, crashing the process, because error events have special handling requirements in the EventEmitter API.

Quick fix

Fixes ERR_UNHANDLED_ERROR

const stream = fs.createReadStream('file.txt');

// WRONG - error may fire before listener is attached
stream.pipe(destination);
stream.on('error', (err) => {
  console.error('Stream error:', err);
});

// CORRECT - attach error handler first
stream.on('error', (err) => {
  console.error('Stream error:', err);
});
stream.pipe(destination);
  1. Attach error event listener before any operations
  2. Handle errors on child processes
  3. Handle errors on HTTP client requests
  4. Create custom EventEmitter subclasses with error handling
node.js — error
const stream = fs.createReadStream('file.txt');
// WRONG - error may fire before listener is attached
stream.pipe(destination);
stream.on('error', (err) => {
console.error('Stream error:', err);
});
// CORRECT - attach error handler first
stream.on('error', (err) => {
console.error('Stream error:', err);
});
stream.pipe(destination);
Error: Unhandled error event (no error handler)

What this error means

Node.js EventEmitters treat "error" events specially. Unlike other events, if an "error" event is emitted and no listener is attached to handle it, Node.js throws the error as an uncaught exception, terminating the entire process. This is by design: error events represent exceptional conditions that should not be silently ignored. If your code emits an error event, you must either: 1. Attach an error handler before the error is emitted 2. Use try-catch (if using async/await with promisified emitters) 3. Prevent the error condition from occurring The error commonly occurs with streams (readable, writable, duplex), child processes, HTTP clients, and custom EventEmitter subclasses. Any code that emits error events must have protection for handling them.

Typical symptoms

  • Process crashes unexpectedly with "Error: Unhandled error event" message
  • Error happens at unpredictable times after application starts
  • Works in development but crashes in production under certain load conditions
  • Stream operations fail and crash the entire Node.js process
  • Child process errors cause parent process to exit
  • HTTP requests fail and terminate the application
  • No stack trace or clear indication of which event emitter caused the issue
  • Works when code is run with a debugger but crashes in normal execution

Common causes

  1. Error handler not attached to EventEmitter before error is emitted
  2. Error handler is attached after the error event is emitted (race condition)
  3. Stream operations fail but error handler only listening on "error" event of wrong stream
  4. Child process spawned without error event handler on the ChildProcess object
  5. Event emitter subclass does not initialize event handlers in constructor
  6. Removing error handler (using removeListener) before all errors are processed
  7. Async code emitting error events before await completes
  8. Custom EventEmitter subclass emitting error before super() initializes listeners

How to fix "Error: Unhandled error event (no error handler)"

1Attach error event listener before any operations

Always attach an error handler immediately after creating or obtaining an EventEmitter:

javascript
const stream = fs.createReadStream('file.txt');

// WRONG - error may fire before listener is attached
stream.pipe(destination);
stream.on('error', (err) => {
  console.error('Stream error:', err);
});

// CORRECT - attach error handler first
stream.on('error', (err) => {
  console.error('Stream error:', err);
});
stream.pipe(destination);

For streams, attach error handlers to ALL streams involved:

javascript
const fs = require('fs');

const readable = fs.createReadStream('source.txt');
const writable = fs.createWriteStream('dest.txt');

// Handle errors on both streams
readable.on('error', (err) => {
  console.error('Read error:', err);
});

writable.on('error', (err) => {
  console.error('Write error:', err);
});

readable.pipe(writable);
2Handle errors on child processes

Child process objects require explicit error handling:

javascript
const { spawn } = require('child_process');

const child = spawn('some-command', ['arg1', 'arg2'']);

// WRONG - missing error handler
child.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

// CORRECT - add error handler
child.on('error', (err) => {
  console.error('Child process error:', err);
});

child.on('exit', (code) => {
  console.log(`Process exited with code ${code}`);
});

child.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

Handle stdio stream errors separately:

javascript
const child = spawn('command');

// Process error
child.on('error', (err) => {
  console.error('Process error:', err.message);
});

// Stream errors
child.stdout.on('error', (err) => {
  console.error('stdout error:', err);
});

child.stderr.on('error', (err) => {
  console.error('stderr error:', err);
});

child.stdin.on('error', (err) => {
  console.error('stdin error:', err);
});
3Handle errors on HTTP client requests

HTTP request objects are EventEmitters that can emit error events:

javascript
const http = require('http');

// WRONG - missing error handler
const req = http.get('http://example.com', (res) => {
  res.on('data', (chunk) => {
    console.log(chunk);
  });
});

// CORRECT - handle request errors
const req = http.get('http://example.com', (res) => {
  res.on('data', (chunk) => {
    console.log(chunk);
  });

  res.on('error', (err) => {
    console.error('Response error:', err);
  });
});

req.on('error', (err) => {
  console.error('Request error:', err.message);
});

Using modern async/await:

javascript
const https = require('https');

const req = https.request('https://api.example.com/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' }
}, (res) => {
  let data = '';
  res.on('data', (chunk) => { data += chunk; });
  res.on('end', () => console.log(data));
  res.on('error', (err) => console.error('Response error:', err));
});

req.on('error', (err) => {
  console.error('Request error:', err.message);
});

req.write(JSON.stringify({ key: 'value' }));
req.end();
4Create custom EventEmitter subclasses with error handling

When extending EventEmitter, ensure error handlers are set up in the constructor:

javascript
const EventEmitter = require('events');

// WRONG - error emitted without handler
class DataProcessor extends EventEmitter {
  constructor() {
    super();
  }

  process(data) {
    if (!data) {
      this.emit('error', new Error('No data provided'));
    }
  }
}

const processor = new DataProcessor();
processor.process(null); // Crashes!

// CORRECT - error handler attached in constructor
class DataProcessor extends EventEmitter {
  constructor() {
    super();

    this.on('error', (err) => {
      console.error('Processing error:', err.message);
    });
  }

  process(data) {
    if (!data) {
      this.emit('error', new Error('No data provided'));
    } else {
      this.emit('success', data);
    }
  }
}

const processor = new DataProcessor();
processor.process(null); // Handled gracefully
5Use try-catch with promisified EventEmitters

Convert EventEmitter error handling to promises for cleaner async/await code:

javascript
const fs = require('fs');
const { promisify } = require('util');
const pipeline = promisify(require('stream').pipeline);

async function processFile() {
  try {
    const readable = fs.createReadStream('input.txt');
    const writable = fs.createWriteStream('output.txt');

    await pipeline(readable, writable);
    console.log('File processed successfully');
  } catch (err) {
    console.error('Pipeline error:', err.message);
  }
}

processFile();

For custom EventEmitters:

javascript
function emitterToPromise(emitter, successEvent = 'success', errorEvent = 'error') {
  return new Promise((resolve, reject) => {
    emitter.once(successEvent, resolve);
    emitter.once(errorEvent, reject);
  });
}

try {
  const result = await emitterToPromise(processor);
  console.log('Result:', result);
} catch (err) {
  console.error('Error:', err.message);
}
6Check error handler is attached to correct object

Make sure error handlers are on the right EventEmitter object:

javascript
const fs = require('fs');

// WRONG - error handler on writable, but error comes from readable
const readable = fs.createReadStream('input.txt');
const writable = fs.createWriteStream('output.txt');

writable.on('error', (err) => {
  console.error('Error:', err);
});

readable.pipe(writable); // Error on readable won't be caught

// CORRECT - handle both
readable.on('error', (err) => {
  console.error('Read error:', err);
});

writable.on('error', (err) => {
  console.error('Write error:', err);
});

readable.pipe(writable);

For HTTP requests:

javascript
const http = require('http');

const req = http.get(url, (res) => {
  res.on('error', (err) => {
    console.error('Response stream error:', err);
  });
});

req.on('error', (err) => {
  console.error('Request error:', err);
});
7Add defensive checks for error handlers

Check if error handlers exist before emitting:

javascript
class EventEmitterWithDefense extends EventEmitter {
  emitError(error) {
    if (this.listenerCount('error') > 0) {
      this.emit('error', error);
    } else {
      console.error('Unhandled error in', this.constructor.name, ':', error);
    }
  }
}

const emitter = new EventEmitterWithDefense();
emitter.emitError(new Error('Something went wrong')); // Won't crash

Alternatively, set a default error handler:

javascript
function safeEmitter(emitter) {
  if (emitter.listenerCount('error') === 0) {
    emitter.on('error', (err) => {
      console.error('Unhandled error event:', err.message);
    });
  }
  return emitter;
}

const stream = fs.createReadStream('file.txt');
safeEmitter(stream);

Advanced notes

Understanding Error Event Semantics

The "error" event is special because Node.js throws an uncaught error if it's emitted with no listeners. This is intentional: error events represent exceptional conditions and should never be silently ignored.

Race Conditions with Async Operations

Errors can be emitted asynchronously:

javascript
const stream = fs.createReadStream('file.txt');

setImmediate(() => {
  stream.on('error', (err) => {
    console.error(err);
  });
});

stream.pipe(destination); // Error may fire immediately

// Solution - attach handler synchronously
stream.on('error', (err) => {
  console.error(err);
});

setImmediate(() => {
  stream.pipe(destination);
});

Memory Leaks from Error Handlers

Attaching error handlers without cleanup can cause memory leaks:

javascript
// CORRECT - clean up handlers
function processStream(filePath) {
  const stream = fs.createReadStream(filePath);

  const errorHandler = (err) => {
    console.error(err);
    stream.removeListener('error', errorHandler);
  };

  stream.on('error', errorHandler);
  return stream;
}

EventEmitter Subclasses

When extending EventEmitter, decide whether errors should bubble up:

javascript
class DataFetcher extends EventEmitter {
  async fetch(url) {
    try {
      const response = await fetch(url);
      const data = await response.json();
      this.emit('data', data);
    } catch (err) {
      if (this.listenerCount('error') > 0) {
        this.emit('error', err);
      } else {
        throw err;
      }
    }
  }
}

Best Practices

- Always attach error handlers synchronously before operations
- Use promises/async-await for cleaner error handling
- Check listenerCount('error') before emitting errors
- Clean up event listeners to prevent memory leaks
- Use removeListener() or removeAllListeners() when appropriate

Frequently asked questions

What does "ERR_UNHANDLED_ERROR" mean?
js EventEmitters treat "error" events specially. js throws the error as an uncaught exception, terminating the entire process. This is by design: error events represent exceptional conditions that should not be silently ignored.
How do I fix "ERR_UNHANDLED_ERROR"?
This error occurs when an EventEmitter instance emits an "error" event but no listener is registered to handle it. Node.js will throw an unhandled error, crashing the process, because error events have special handling requirements in the EventEmitter API.
What causes "ERR_UNHANDLED_ERROR"?
Error handler not attached to EventEmitter before error is emitted Error handler is attached after the error event is emitted (race condition) Stream operations fail but error handler only listening on "error" event of wrong stream
Is the "ERR_UNHANDLED_ERROR" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading