Merging stack traces in rethrown errors - javascript

I'm rethrowing here an error from Sequelize promise (Bluebird). In the first place, this was done to change error message, but as it appeared, this also produces more informative stack trace.
It is something like
sequelize.sync().catch(originalError => {
const rethrownError = new Error(originalError.msg + ': ' + originalError.sql);
throw rethrownError;
});
Where originalError.stack doesn't contain the line that caused the error but it holds important information that it originates in Sequelize and MySQL driver:
SequelizeDatabaseError: ER_KEY_COLUMN_DOES_NOT_EXITS: Key column 'NonExisting' doesn't exist in table
at Query.formatError (...\node_modules\sequelize\lib\dialects\mysql\query.js:175:14)
at Query._callback (...\node_modules\sequelize\lib\dialects\mysql\query.js:49:21)
at Query.Sequence.end (...\node_modules\mysql\lib\protocol\sequences\Sequence.js:85:24)
at Query.ErrorPacket (...\node_modules\mysql\lib\protocol\sequences\Query.js:94:8)
at Protocol._parsePacket (...\node_modules\mysql\lib\protocol\Protocol.js:280:23)
at Parser.write (...\node_modules\mysql\lib\protocol\Parser.js:74:12)
at Protocol.write (...\node_modules\mysql\lib\protocol\Protocol.js:39:16)
at Socket.<anonymous> (...\node_modules\mysql\lib\Connection.js:109:28)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at Socket.Readable.push (_stream_readable.js:134:10)
at TCP.onread (net.js:548:20)
rethrownError.stack contains the point of interest (the first line in the stack) but everything else is a rubbish:
Error: ER_KEY_COLUMN_DOES_NOT_EXITS: Key column 'NonExisting' doesn't exist in table
at sequelize.sync.catch (...\app.js:59:17)
at tryCatcher (...\node_modules\bluebird\js\release\util.js:16:23)
at Promise._settlePromiseFromHandler (...\node_modules\bluebird\js\release\promise.js:504:31)
at Promise._settlePromise (...\node_modules\bluebird\js\release\promise.js:561:18)
at Promise._settlePromise0 (...\node_modules\bluebird\js\release\promise.js:606:10)
at Promise._settlePromises (...\node_modules\bluebird\js\release\promise.js:681:18)
at Async._drainQueue (...\node_modules\bluebird\js\release\async.js:138:16)
at Async._drainQueues (...\node_modules\bluebird\js\release\async.js:148:10)
at Immediate.Async.drainQueues (...\node_modules\bluebird\js\release\async.js:17:14)
at runCallback (timers.js:637:20)
at tryOnImmediate (timers.js:610:5)
at processImmediate [as _immediateCallback] (timers.js:582:5)
I would like to keep the information about both of them - and to designate the link between them, not just to add as two unrelated log entries.
I've been thinking on logging them as a single error with concatenated stack, rethrownError.stack += '\n' + originalError.stack.
How should these two errors be treated? Should their stack traces be joined? Is there a convention for merging error stacks in JavaScript (Node.js in particular)?
The intention is to keep the resulting error meaningful and to not upset existing tools that parse error stack traces (namely Stacktrace.js).
The projects under consideration use Winston logger or plain console.error, so the error is stringified at some point (in the example above it was logged via unhandled rejection handler).

🚨 UPDATE 2022: Error.prototype.cause has been implemented in ES2022. Consider using the native implementation.
Since ECMAScript 2022, new Error() lets us specify what caused it:
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
// ···
} catch (error) {
throw new Error(
`While processing ${filePath}`,
{cause: error}
);
}
});
}
See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
Original answer:
Here is a lightweight alternative to VError: rerror
(I’m the author)
The idea is the same: Wrapping errors in errors. However it is much simpler. It has less features, but also works in the browser. It also takes into account that creating stack traces is expensive. Instead of creating stack traces and appending them to a string it creates a stack of errors internally and only creates the big stack trace if you need it (use the getter).
Example
function fail() {
throw new RError({
name: 'BAR',
message: 'I messed up.'
})
}
function failFurther() {
try {
fail()
} catch (err) {
throw new RError({
name: 'FOO',
message: 'Something went wrong.',
cause: err
})
}
}
try {
failFurther()
} catch (err) {
console.error(err.why)
console.error(err.stacks)
}
Output
FOO: Something went wrong. <- BAR: I messed up.
Error
at failFurther (/Users/boris/Workspace/playground/es5/index.js:98:11)
at Object.<anonymous> (/Users/boris/Workspace/playground/es5/index.js:107:3)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Module.runMain (module.js:590:10)
at run (bootstrap_node.js:394:7)
<- Error
at fail (/Users/boris/Workspace/playground/es5/index.js:88:9)
at failFurther (/Users/boris/Workspace/playground/es5/index.js:96:5)
at Object.<anonymous> (/Users/boris/Workspace/playground/es5/index.js:107:3)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Module.runMain (module.js:590:10)
A recommended read: https://www.joyent.com/node-js/production/design/errors

As far as I know, there is no built-in way to handle nested errors in Node.js. The only thing I can recommend you is to use the VError library. It is really useful when dealing with advanced error handling.
You can use fullStack to combine stack traces of many errors:
var err1 = new VError('something bad happened');
var err2 = new VError(err1, 'something really bad happened here');
console.log(VError.fullStack(err2));

Building upon https://stackoverflow.com/a/42147148/1703845, I abstracted the VError.fullStack call away like this
class FullStackVError extends VError {
constructor(cause, ...args) {
super(cause, ...args);
let childFullStack;
if (cause instanceof VError) {
childFullStack = cause.stack;
cause.stack = cause._originalStack;
}
this._originalStack = this.stack;
this.stack = VError.fullStack(this);
if (cause instanceof VError) {
cause.stack = childFullStack;
}
}
}
Now console.log(err2.stack); is equivalent to what console.log(VError.fullStack(err2)); would have been.

Related

Mutex timeout in javscript does'nt show on what line in code the error occurs

I use Node v16.14.2 and run this program in the command prompt.
I have a little problem with using Mutex in javascript. The Mutex itself do work but sometimes it takes to long to aquire the Mutex and I run into the error:
C:\myproject\node_modules\async-mutex\lib\errors.js:4
exports.E_TIMEOUT = new Error('timeout while waiting for mutex to become available');
^
Error: timeout while waiting for mutex to become available
at Object.<anonymous> (C:\myproject\node_modules\async-mutex\lib\errors.js:4:21)
at Module._compile (node:internal/modules/cjs/loader:1103:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.<anonymous> (C:\myproject\node_modules\async-mutex\lib\Semaphore.js:4:16)
at Module._compile (node:internal/modules/cjs/loader:1103:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
I have 2 things I wonder about:
The error message doesn't show on what line this error occurs in my code which is a problem because I have perheps 20 different mutexes and I don't know which one has the problem. How to know on what line in my code this error occurs?
(All other types of errors that is not related to the mutex shows which line the error occurs to mention)
Is there a problem to let the code aquire the mutex anyway even if it is not avaliable (which probably is not a good idéa but still?)
Test code to reproduce the error. I have nested the same mutex just to illustrate the error.
var Mutex = require('async-mutex').Mutex;
var withTimeout = require('async-mutex').withTimeout;
const mutexWithTimeout = withTimeout(new Mutex(), 5000); //5 second timeout
_testfunction();
async function _testfunction() {
console.log("starting...");
await mutexWithTimeout.runExclusive(async () => {
console.log("I am inside the mutex now...");
await mutexWithTimeout.runExclusive(async () => {
console.log("Timeout will happen here because I am nested just to reproduce the error");
});
});
}

Best practice JavaScript rethrow preserving inner exception [duplicate]

This question already has answers here:
How to chain exceptions in javascript (ie add cause like in java)
(6 answers)
Closed 2 years ago.
function doFoo() {
throw new Error("no foo for you");
}
try {
doFoo();
} catch(e) {
throw new Error("doFoo() for shame!", e);
}
Is there any sane way to do this in JavaScript?
My specific objectives for the particular use case I'm asking this question are the following, in this order:
Follow standards, or at least best practices
Throw a new exception/error/whatever you want to call it (not just rethrow the thing I just caught); I'll call this an exception from now on
Preserve the message in the previous exception (I'd like to preserve the stack trace, but that's not as important for this particular use case)
Add extra metadata to the previous exception
Preserve the stack trace of the previous exception (nice to have, not really important to me right now)
How about a simple wrapper ?
class RootCause extends Error {
constructor(message, error) {
super(message);
if (error) {
this.cause = error;
}
}
}
function doFoo() {
throw new RootCause("no foo for you");
}
try {
doFoo();
} catch(e) {
throw new RootCause("doFoo() for shame!", e)
}
/** Prints:
RootCause [Error]: doFoo() for shame!
at Object.<anonymous> (/private/tmp/test.js:17:11)
at Module._compile (internal/modules/cjs/loader.js:1138:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
at Module.load (internal/modules/cjs/loader.js:986:32)
at Function.Module._load (internal/modules/cjs/loader.js:879:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
at internal/main/run_main_module.js:17:47 {
cause: RootCause [Error]: no foo for you
at doFoo (/private/tmp/test.js:11:11)
at Object.<anonymous> (/private/tmp/test.js:15:5)
at Module._compile (internal/modules/cjs/loader.js:1138:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
at Module.load (internal/modules/cjs/loader.js:986:32)
at Function.Module._load (internal/modules/cjs/loader.js:879:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
at internal/main/run_main_module.js:17:47
**/

is there a problem with this expression {item_1.name: req.body.item } or alternatively with this {[item_1.name]: req.body.item }?

I'm trying to run this piece of code:
router.put('/restaurants/:id', async (req, res) => {
try {
const response = await Restaurant.findByIdAndUpdate(req.params.id, {name: req.body.name,
item_1.name: req.body.item,
item_2.name: req.body.item_2,
item_3.name: req.body.item_3,
item_4.name: req.body.item_4
})
res.send(response)
}
catch (error) {
res.send(error)
}
})
where the scenario is I have items (i.e item_1 etc) saved as objects in database, items got two properties name and rating, when admin wants to edit an item it should only be able to just edit the name property of an item not the rating, so for implementing this what i'm trying to do is, upon edit request as shown here, I want to set only the name property of an item as same to what has been sent in the request.
but it gives me a typescript error (though I don't have typescript installed) saying:
',' expected.ts(1005)
and it happens before running this code, actually vs code is showing this error.
and upon running it is showing something like this:
E:\xord\second_assignment\node\routes\restaurants.js:50
item_1.name: req.body.item,
^
SyntaxError: Unexpected token '.'
at wrapSafe (internal/modules/cjs/loader.js:1054:16)
at Module._compile (internal/modules/cjs/loader.js:1102:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
at Module.load (internal/modules/cjs/loader.js:986:32)
at Function.Module._load (internal/modules/cjs/loader.js:879:14)
at Module.require (internal/modules/cjs/loader.js:1026:19)
at require (internal/modules/cjs/helpers.js:72:18)
at Object.<anonymous> (E:\xord\second_assignment\node\index.js:8:21)
at Module._compile (internal/modules/cjs/loader.js:1138:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
at Module.load (internal/modules/cjs/loader.js:986:32)
at Function.Module._load (internal/modules/cjs/loader.js:879:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
at internal/main/run_main_module.js:17:47
[nodemon] app crashed - waiting for file changes before starting...
same thing happens with a different error when i try to access the object property with bracket notation.
I apologise for the very long query but I'm wondering; is the syntax I've used in this code for setting the value of an object's key inside another object, incorrect? if so then why? also what would be the alternative way to do this?
thanks God! in mongoose v5.10.19 documentation I saw almost the same instance where they use a property of an object as a key of another object here:
Parent.update({}, { 'child.name': 'Luke Skywalker' }, (error) => {
// Error because parentSchema has `strict: throw`, even though
// `childSchema` has `strict: false`
});
by which I learnt that in such cases one should wrap the key in quotes as they did in "child.name". and that resolved the issue i was facing.

Creating JS library Warning instead of Error

I am creating my own JS library with NPM. I want to give the users of my library a warning but not an error, however, I want my users to have a stacktrace of the warning that I log inside my library. So to get the stacktrace, I use new Error().stack. The problem with this is (1) the word 'Error' appears in the stacktrace by default and (2) the stack trace is the same for Node and Chrome but different for Mozilla.
So my question is - what is the best way to create a warning message that has a stacktrace, but that doesn't look like an error? Chrome and Node.js are aligned and I could simply remove the first element of the stack array before calling toString() on it. But the problem is that this wouldn't work for Mozilla.
Anyway, this is what I currently do in my library when there should be a warning emitted:
var err = new Error('looks like you have called IdempotentJSON.parse on an object that was already parsed');
console.error(err.message.concat('\n'),err.stack);
this "logs a warning" that looks like this
in Chrome
looks like you have called IdempotentJSON.parse on an object that was already parsed
Error: looks like you have called IdempotentJSON.parse on an object that was already parsed
at IdempotentJSON.parse (http://localhost:3000/static/vendor/idempotent-json.js:11:23)
at Backbone.Model.extend.persistModel.save.success (http://localhost:3000/static/app/js/models/userModel.js:88:56)
at Object._.extend.save.options.success (http://localhost:3000/static/vendor/backbone.js:620:30)
at jQuery.Callbacks.fire (http://localhost:3000/static/vendor/jquery.js:3099:30)
at Object.jQuery.Callbacks.self.fireWith [as resolveWith] (http://localhost:3000/static/vendor/jquery.js:3211:7)
at done (http://localhost:3000/static/vendor/jquery.js:8264:14)
at XMLHttpRequest.jQuery.ajaxTransport.send.callback (http://localhost:3000/static/vendor/jquery.js:8605:9)
in Mozilla Firefox:
"looks like you have called IdempotentJSON.parse on an object that was already parsed
" "IdempotentJSON.prototype.parse#http://localhost:3000/static/vendor/idempotent-json.js:11:23
#http://localhost:3000/static/app/js/app.js:157:30
Backbone.batchSyncCollection/<#http://localhost:3000/static/app/js/app.js:141:16
jQuery.Callbacks/fire#http://localhost:3000/static/vendor/jquery.js:3099:10
jQuery.Callbacks/self.fireWith#http://localhost:3000/static/vendor/jquery.js:3211:7
done#http://localhost:3000/static/vendor/jquery.js:8264:5
.send/callback/<#http://localhost:3000/static/vendor/jquery.js:8605:1
in Node.js
looks like you have called IdempotentJSON.parse on an object that was already parsed
Error: looks like you have called IdempotentJSON.parse on an object that was already parsed
at IdempotentJSON.parse (/Users/amills001c/WebstormProjects/first_comcast/node_modules/idempotent-json/index.js:9:19)
at Object.<anonymous> (/Users/amills001c/WebstormProjects/first_comcast/IJSONexp/ijson.js:8:15)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
one quick solution that would work for Node.js and Chrome would be to remove the first line of the stack trace that says "Error:...".
But this call is not valid:
err.stack.shift(); //error
apparently the stack is not an Array in JS?
And this wouldn't work for Mozilla anyway.
Is my best bet another library that checks which environment/browser that it's in? Like this:
http://www.eriwen.com/javascript/js-stack-trace/
it's 2015! By the way Mozilla, Chrome and Node have it right with the stack trace, I can click on the stack trace in Chrome and Node and it takes me to the files involved, doesn't seem like I can do this in Firefox.
What I want is something that looks like this:
Warning: looks like you have called IdempotentJSON.parse on an object that was already parsed
at IdempotentJSON.parse (http://localhost:3000/static/vendor/idempotent-json.js:11:23)
at Backbone.Model.extend.persistModel.save.success (http://localhost:3000/static/app/js/models/userModel.js:88:56)
at Object._.extend.save.options.success (http://localhost:3000/static/vendor/backbone.js:620:30)
at jQuery.Callbacks.fire (http://localhost:3000/static/vendor/jquery.js:3099:30)
at Object.jQuery.Callbacks.self.fireWith [as resolveWith] (http://localhost:3000/static/vendor/jquery.js:3211:7)
at done (http://localhost:3000/static/vendor/jquery.js:8264:14)
at XMLHttpRequest.jQuery.ajaxTransport.send.callback (http://localhost:3000/static/vendor/jquery.js:8605:9)
Looking into the source of console.trace you can try this for chrome & node.js:
function warning(message) {
var err = new Error;
err.name = 'Warning';
err.message = message; //or use util.format in nodejs
Error.captureStackTrace(err, warning); // this is to prevent `warning` function being included in the stack.
return err;
};
console.log(warning('message').stack);
In nodejs, this outputs:
Warning: message
at Object.<anonymous> (/home/ubuntu/workspace/warning.js:9:13)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
Unfortunately firefox doesn't have captureStackTrace method. So you have to call the above directly without wrapping it in a function.

Missing stack trace on node.js uncaughtException generated by throw()

I'm trying to catch the stack trace of an node.js uncaughtException and it works fine for different errors but not for throw() statements:
Correct stack trace on exception handling:
$ cat errorFunc.js
process.on('uncaughtException', function(exception) {
console.log('uncaughtException occurred: ' + exception.stack);
});
MyError();
$ node errorFunc.js
uncaughtException occurred: ReferenceError: MyError is not defined
at Object.<anonymous> (/home/jolcese/code/WebEnclaves/Server/errorFunc.js:5:1)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:901:3
$
Missing stack trace on exception caused by throw():
$ cat errorThrow.js
process.on('uncaughtException', function(exception) {
console.log('uncaughtException occurred: ' + exception.stack);
});
throw('my error');
$ node errorThrow.js
uncaughtException occurred: undefined
$
Any idea why?
Thanks
Jose
Disclaimer: I know that using process.on('uncaughtException') is a very, very bad thing and I will be punished but using domains is not an option in this code.
JavaScript lets you throw anything.
If you want to throw errors with stack traces in JavaScript, you need to throw Error objects.
(specification )
Also, throw is an operator and not a function.
Try
throw new Error('my error');
See the manual on Mozilla Developer Network for more information.

Categories

Resources