When handling errors, always ensure the original error context is preserved and properly surfaced. Error information should never be silently swallowed in try/catch blocks, as this makes debugging extremely difficult and can lead to unexpected system behavior.
When handling errors, always ensure the original error context is preserved and properly surfaced. Error information should never be silently swallowed in try/catch blocks, as this makes debugging extremely difficult and can lead to unexpected system behavior.
There are two recommended approaches to preserving error context:
error.cause
property to maintain error chains:try {
// Original operation
const result = func(error, reply.request, reply);
// Process result...
} catch (err) {
// Preserve original error context
if (!Object.prototype.hasOwnProperty.call(err, 'cause')) {
err.cause = error; // Link the original error as the cause
} else {
// Log when unable to set cause to avoid losing context
logger.warn({
err: error,
parentError: err,
message: 'Original error cannot be linked as cause'
});
}
// Propagate the error with context preserved
throw err;
}
// BAD: Double resolution if error occurs
sget(options, (err, response, body) => {
if (err) reject(err)
resolve({ response, body }) // Still runs after reject!
})
// GOOD: Return after rejection to prevent double resolution
sget(options, (err, response, body) => {
if (err) {
reject(err)
return // Important! Prevents executing the resolve
}
resolve({ response, body })
})
Remember that proper error handling includes validation of your error conditions themselves. Be careful with falsy checks that might incorrectly handle empty strings or zero values as error conditions.
Enter the URL of a public GitHub repository