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

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.

Related

Merging stack traces in rethrown errors

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.

Node.js assert module: message parameter meaning

Reading Node.js assert's module documentation I have been failing to understand what is the meaning of the message parameter and how should I use it.
If I write an assertion like:
assert(container.search('asd') === undefined, "Can't find [asd]");
When runnning the code, if it fails, I just get an exception at the assertion line, and can't see the message:
assert.js:86
throw new assert.AssertionError({
^
AssertionError: false == true
at Object.<anonymous> (c:\tests\Container.js:70:1)
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
The message argument simply defines what the error message will be if the test fails. This is common across most assertion libraries.
Example:
assert(false, 'hello world')
throws the error
AssertionError: hello world

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.

Node.js https.createServer throws TypeError: listener must be a function

I've read posts all over concerning this and I know it must be something silly, but I can't figure out why the following code is throwing "TypeError: listener must be a function"
Assume options
var server = https.createServer(options, function(request,response){
if (request.url==='/') request.url='/home/altronic/Opti-Cal/web/arimonitor.htm';
console.log("Request: " + request.url);
fs.readFile("public"+request.url,function(error,data){
if (error) {
response.writeHead(404, {"Content-type":"text/plain"});
response.end ("Sorry the page you requested was not found.");
} else {
response.writeHead(200,{"Content-type":mime.lookup('public'+request.url)});
response.end (data);
}
})
}).listen(port);
Console output:
events.js:130
throw TypeError('listener must be a function');
^
TypeError: listener must be a function
at TypeError (<anonymous>)
at Server.EventEmitter.addListener (events.js:130:11)
at new Server (http.js:1816:10)
at Object.exports.createServer (http.js:1846:10)
at Object.<anonymous> (/home/altronic/Opti-Cal/src/Opti-Cal_HTTPS_Server.js:42:20)
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)
Can anyone help me figure this out?
Where do you assign https? It looks like you’re probably requiring http, not https. http.createServer doesn’t accept options like https.createServer.
You may hit this error when using a node version < 9.6
See the docs and history. I was very confused that the docs said I could use an options object on http.createServer and got this error until I realized I hadn't updated node in a while.
https://nodejs.org/api/http.html#http_http_createserver_options_requestlistener
I had the same error message:
throw TypeError('listener must be a function');
I have two separate files server.js and handler.js.
My problem was whilst I was requiring ( require(./handler.js) )my handler file in server.js I was not exporting it from handler.js file. You must have: module.exports =handler; at the bottom of your handler file
This is mostly coming from a version mismatch. Latest versions of nodejs's http.createServer() do take options in parameters like https does.

Node.js: console.log message doesn't show up if method throws exception... why?

In Node.js, if I have a method that throws an exception, console.log statements from that method don't fire. I recognize that in the simple test case below that I should catch the exception from the readFileSync call, or otherwise be defensive about it. Just curious if someone could explain the behavior to me.
Simple test case:
var fs = require('fs');
function readAFileThatDoesntExist(filename) {
console.log(filename);
fs.readFileSync(filename);
}
console.log("We're about to read a file that doesn't exist!");
readAFileThatDoesntExist("afile");
Output:
$ node test.js
We're about to read a file that doesn't exist!
fs.js:338
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
^
Error: ENOENT, no such file or directory 'C:\blog\projects\bloggen\scripts\afile'
at Object.fs.openSync (fs.js:338:18)
at Object.fs.readFileSync (fs.js:182:15)
at readAFileThatDoesntExist (C:\blog\projects\bloggen\scripts\test.js:5:8)
at Object.<anonymous> (C:\blog\projects\bloggen\scripts\test.js:9:1)
at Module._compile (module.js:449:26)
at Object.Module._extensions..js (module.js:467:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.runMain (module.js:492:10)
at process.startup.processNextTick.process._tickCallback (node.js:244:9)
Ah, figured it out.
It seems that console.log isn't finishing before the process exits... If I use console.warn, the message does show up.
This post explains it: is node.js' console.log asynchronous?
Also, I'm on an older version (0.8.15), so this may no longer be relevant.

Categories

Resources