programming.protips.wiki
githubtwitter

Chain custom errors to preserve stack traces

#error-handling

Don't gobble up underlying errors when (re)throwing a custom error. Instead, chain the original error to preserve its stack trace in the output.

Bad Example

try {
    doSomethingCool();
} catch (err) {
    throw new Error('Yikes! Failed to do anything cool :(');
}

Prefer

If you don't want to do extra error handling to print the full stack trace, and want something that "just works" (for one-off scripts):

import ono from '@jsdevtools/ono';

try {
    doSomethingCool();
} catch (err) {
    throw ono(err, 'Yikes! Failed to do anything cool :(');
}

If you don't want extra libraries and want to use the new built-in spec, then you do this:

try {
    doSomethingCool();
} catch (err) {
    throw new Error('Yikes! Failed to do anything cool :(', { cause: err });
}

Note: By default this will just print the last trace to the console - you'll need to do a top-level catch-all clause and format the combined stack trace to view the full trace (similar to VError.fullStack(). If you don't want to set this up, use something like ono as shown above.

Why?

The original error object (err) contains valuable stack trace information that will indicate why the operation failed. By simply throwing a new error and discarding err, we gobble up that information and prevent the programmer from ever seeing it.

Sometimes we want to throw custom errors - remember to throw a combined error including the underlying error object if applicable.

(This is also known as "exception chaining" in Python.)

Resources for chaining errors: