Reputation: 483
I have a background of Java developer and I'm quite new to JavaScript/TypeScript.
Is there a standard way to manage and keep the cause of an Error in JavaScript/TypeScript?
My purpose is to get a complete stacktrace when I wrap an Error into another ; a little bit as with Java Exception stacktrace:
Message of exception 1
...
Caused by: Message of exception 2
...
Caused by: Message of the root exception
I try this code but err1
doesn't keep the reference of err2
:
// CODE
try {
try {
throw new Error("Error no2");
} catch (err2) {
console.log("========================================");
console.log(err2);
throw new Error("Error no1");
}
} catch (err1) {
console.log("========================================");
console.log(err1);
}
// CONSOLE OUTPUT
$ node test.ts
========================================
Error: Error no2
at Object.<anonymous> (/tmp/test.ts:3:15)
at Module._compile (internal/modules/cjs/loader.js:956:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
at Module.load (internal/modules/cjs/loader.js:812:32)
at Function.Module._load (internal/modules/cjs/loader.js:724:14)
at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
at internal/main/run_main_module.js:17:11
========================================
Error: Error no1
at Object.<anonymous> (/tmp/test.ts:7:15)
at Module._compile (internal/modules/cjs/loader.js:956:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
at Module.load (internal/modules/cjs/loader.js:812:32)
at Function.Module._load (internal/modules/cjs/loader.js:724:14)
at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
at internal/main/run_main_module.js:17:11
Besides, I don't find any property that is named cause
in Error class. There is a stack
property but I think it's a bad practice to change it.
Thanks!
Upvotes: 14
Views: 14374
Reputation: 5696
I use the Error cause option a lot. My text building function that the loggers use looks like this
export function getErrorMessageWithCausedByParts(err: Error, stack=true): string {
const doNewLines = stack;
let txt = "";
if(stack) {
txt+=err.stack;
} else {
txt+=err.message;
}
if(err.cause != null) {
if (err.cause instanceof Error) {
if(doNewLines) {
txt+="\n[caused by] ";
} else {
txt += " [caused by] ";
}
txt += getErrorMessageWithCausedByParts(err.cause, stack);
} else {
txt += `[Unknown cause type?!]: ${JSON.stringify(err.cause)}`
}
}
return txt;
}
Note: to be able to use the cause option of the Error object you have to set a newer ES version in your tsconfig.json
"compilerOptions": {
"lib": ["es2022"]
...
}
Upvotes: 0
Reputation: 1492
2021 answer: There is now a standard way of managing and keeping the cause an error. The Error()
constructor can now accept an options
object as a second argument. This options
object should have a cause
property which would typically be another Error
but it doesn't have to be.
Example
throw new Error('Outer Error', {cause: new Error('Inner Error')});
Support
At the time of writing (October 2021) there's support for the new constructor in Firefox, Chrome and Safari. To get the cause of an Error
you'll need to interrogate it's cause
property. Additionally in Firefox we get a bit of extra info in the console, e.g
Uncaught Error: Outer Error
<anonymous> debugger eval code:1
Caused by: Error: Inner Error
<anonymous> debugger eval code:1
Edge will currently just ignore the second constructor property.
Sources
Mozilla doc: https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error
ECMAScript proposal: https://github.com/tc39/proposal-error-cause
Upvotes: 11
Reputation: 1219
I've always used a modification of Kristian's answer here
class TraceableError extends Error {
trace: Error;
constructor(message?: string, innerError?: Error) {
super(message);
this.trace = innerError;
const actualProto = new.target.prototype;
if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); }
else { this.__proto__ = actualProto; }
}
}
Then you would throw like this
try {
try {
throw new Error("Error no2");
} catch (err2) {
console.log("========================================");
console.log(err2);
throw new TraceableError ("Error no1", err2);
}
} catch (err1) {
console.log("========================================");
console.log(err1);
}
Note that, if not caught, the
trace
part of the new error will not be output to the console.
Upvotes: 1