How to handle the Unhandled exceptions

Handling unhandled exceptions in Node.js is crucial for building robust and stable applications. Unhandled exceptions occur when an error is thrown that isn’t caught by any try...catch block or other error-handling mechanisms. If not properly managed, these exceptions can crash your Node.js process.

Methods to Handle Unhandled Exceptions

1. Global Exception Handling with process.on('uncaughtException', ...)

Node.js provides a way to catch unhandled exceptions globally using the uncaughtException event. This event is emitted when an exception bubbles all the way up without being caught.

Example:


process.on('uncaughtException', (err) => {
    console.error('There was an uncaught error:', err);
    // Perform cleanup tasks if necessary
    process.exit(1); // Exit the process after handling the exception
});

// Example of an unhandled exception
setTimeout(() => {
    throw new Error('Oops! Something went wrong.');
}, 1000);

Important:

Exiting the Process: After catching an unhandled exception, it’s usually recommended to exit the process (process.exit(1)), as the state of the application may be compromised.

2. Promise Rejection Handling with process.on('unhandledRejection', ...)

Example:

With the rise of Promises in JavaScript, unhandled promise rejections became a common source of issues. Node.js provides a way to catch these globally.


process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
    // Perform cleanup tasks if necessary
    process.exit(1); // Exit the process after handling the rejection
});

// Example of an unhandled promise rejection
new Promise((resolve, reject) => {
    reject(new Error('Promise rejection error!'));
});

3. Graceful Shutdown

When an unhandled exception occurs, it’s often better to shut down the application gracefully after logging the error and performing necessary cleanups.


process.on('uncaughtException', (err) => {
    console.error('Unhandled Exception:', err);
    // Perform cleanup
    server.close(() => {
        process.exit(1); // Exit after closing the server
    });
});

process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection:', reason);
    // Perform cleanup
    server.close(() => {
        process.exit(1); // Exit after closing the server
    });
});

4. Use a Logger

Implement a logging mechanism to keep track of errors. Libraries like winston or bunyan can be useful for this purpose.


const winston = require('winston');
const logger = winston.createLogger({
    level: 'error',
    format: winston.format.json(),
    transports: [
        new winston.transports.File({ filename: 'error.log' })
    ]
});

process.on('uncaughtException', (err) => {
    logger.error('Unhandled Exception:', err);
    process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
    logger.error('Unhandled Rejection:', reason);
    process.exit(1);
});