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);
});