Reputation: 641
I a while ago i used to use Verror from joyent to not lose the stacktrace when rethrowing, I have just done a test with node v12 without Verror and stack trace seems to be persisted without the use of error.
I then was looking at the use of Error.captureStackTrace, it states to use this in your errors so that the Error is not added to the stack.
I don't what i am doing wrong but with or without Error.captureStackTrace - the stack trace is the same..
I would like to know the current status of the use of the captureStackTrace as i see no difference :-) and also the use of VError that doesn't seem to be needed anymore.
In the documentation it states that the .stack isn't available without calling the captureStackTrace but I see it available each time, with or without the captureStackTrace
here is an example of 2 x errors
class MyErrorOne extends Error {
constructor(message) {
super(message);
Error.captureStackTrace(this, this.constructor);
}
}
class MyErrorTwo extends Error {
constructor(message) {
super(message);
Error.captureStackTrace(this, this.constructor);
}
}
I tried them by commenting out the captureStackTrace in EACH error and the stacktrace is the same.
Can anyone help ?
Here is my code to test calling the errors
const DoOne = () => {
try {
console.log("executing do one");
DoTwo();
} catch (error) {
console.log("error in DoOne", error);
console.log("here is the stack ", error.stack);
throw new MyErrorOne("threw error from doone in myerrorone");
// throw error;
}
};
const DoTwo = () => {
try {
console.log("executing do two");
throw new MyErrorTwo("threw error from dotwo in myerrortwo");
} catch (error) {
console.log("error in DoTwo", error);
throw error;
}
};
DoOne();
Upvotes: 31
Views: 70413
Reputation: 49361
captureStackTrace
returns a string that represents the location of that particular error in the call. It gives us a stack that helps us to find the location of that error in the code at which new Error()
was Called. this will help us to find the exact error in our code.
I am not sure which documentation you mention, but if you read V8 stack trace docs
Error.captureStackTrace(error, constructorOpt)
adds a stack property to the given error object that yields the stack trace at the time captureStackTrace was called. Stack traces collected through Error.captureStackTrace are immediately collected, formatted, and attached to the given error object.
The optional constructorOpt parameter allows you to pass in a function value. When collecting the stack trace all frames above the topmost call to this function, including that call, are left out of the stack trace. This can be useful to hide implementation details that won’t be useful to the user. The usual way of defining a custom error that captures a stack trace would be:
function MyError() {
// if you are calling this in constructor, instead of MyError, "this.constructor"
Error.captureStackTrace(this, MyError);
// Any other initialization goes here.
}
Passing in MyError as a second argument means that the constructor call to MyError won’t show up in the stack trace.
Upvotes: 1
Reputation: 1263
When calling Error.captureStackTrace(obj[, fun])
a formatted call stack is attached to obj
including the line/frame where captureStackTrace
was called.
When specifying fun
all frames above and including fun
are removed.
Note: In sample outputs I have removed anything below call to fun1
to minimize clutter.
const fun1 = () => { fun2(); };
const fun2 = () => { fun3(); };
const fun3 = () => { log_stack(); };
function log_stack() {
let err = {};
Error.captureStackTrace(err);
console.log(err.stack);
}
fun1();
This yields:
Error
at log_stack (/path/to/example.js:6:8)
at fun3 (/path/to/example.js:3:22)
at fun2 (/path/to/example.js:2:22)
at fun1 (/path/to/example.js:1:22)
Now, if we add log_stack
as the function option to captureStackTrace
:
Error.captureStackTrace(err, log_stack);
It yields:
Error
at fun3 (/path/to/example.js:3:22)
at fun2 (/path/to/example.js:2:22)
at fun1 (/path/to/example.js:1:22)
The log_stack
frame is not there anymore.
Adding fun3
to the captureStackTrace
:
Error.captureStackTrace(err, fun3);
It yields:
Error
at fun2 (/path/to/example.js:2:22)
at fun1 (/path/to/example.js:1:22)
Etc.
In your case, if you change:
Error.captureStackTrace(this, this.constructor);
to:
Error.captureStackTrace(this);
You will see that you get an additional line with the new MyError...
:
error in DoTwo MyErrorTwo: threw error from dotwo in myerrortwo
at new MyErrorTwo (/path/to/testerrorclass.js:10:9) <<== Not removed anymore.
Upvotes: 40
Reputation: 6160
This is my implementation that I found a while back, the key is to restore the prototype chain (typescript):
import {INTERNAL_SERVER_ERROR} from 'http-status-codes';
export class RouteError extends Error {
public statusCode: number;
/**
* Create a new RouteError object to be handled in 'express-async-handler' wrapped middleware.
* @param message {string} [message=An error occurred.] - The json message key to return to the user.
* @param statusCode {number} [statusCode=INTERNAL_SERVER_ERROR] - response code to send.
*/
constructor(message: string = 'An error occurred.', statusCode: number = INTERNAL_SERVER_ERROR) {
super(message); // 'Error' breaks prototype chain here
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
this.statusCode = statusCode;
}
}
Upvotes: 0
Reputation: 99
I'm not sure, but it seems that Error.captureStackTrace
is intended to be used when constructing Error object without extending the Error
class (see also the example provided in the Node docs.
I'm guessing that when you're extending the Error
class, capturing the stack trace is already done by the Error constructor.
I've tested your code and remove the extending of Error and indeed if you don't call captureStackTrace
you will not get a stack trace on your error objects.
I'm not sure what would be the use case of throwing Errors that are not extending the Error class.
Upvotes: 9