I'm somewhat new to javascript, but I wanted to try to understand how stack-tracing works in nodejs. I was a little lazy to look at the source code, so I just consulted the language reference https://www.ecma-international.org/ecma-262/10.0/index.html#sec-runtime-semantics-catchclauseevaluation and tried to see what works. So, here is the mystery: in the repl I get some odd results when catching and throwing.
When I run:
try { throw Error('foo'); } catch (e) { throw e; }
I get as output:
Error: foo
But when I run:
try { throw Error('foo'); } catch (e) { console.log(e); throw e; }
I get as output:
Error: foo
at repl:1:13
at ContextifyScript.Script.runInThisContext (vm.js:50:33)
at REPLServer.defaultEval (repl.js:240:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:468:10)
at emitOne (events.js:121:20)
at REPLServer.emit (events.js:211:7)
at REPLServer.Interface._onLine (readline.js:282:10)
at REPLServer.Interface._line (readline.js:631:8)
Error: foo
at repl:1:13
at ContextifyScript.Script.runInThisContext (vm.js:50:33)
at REPLServer.defaultEval (repl.js:240:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:468:10)
at emitOne (events.js:121:20)
at REPLServer.emit (events.js:211:7)
at REPLServer.Interface._onLine (readline.js:282:10)
at REPLServer.Interface._line (readline.js:631:8)
It seems as though calling console.log(e) causes the error object to trace the execution context stack (I hope I'm using this term correctly), but if it passes through to the repl without the context.log(e) it only knows about its error message.
I've tried different permutations of this and also nesting in different lexical contexts (functions and another try-catch block), and they all seem to give this "funny" behavior. Also, when executing these as scripts I always get an expected error message and stack-trace, so I'm led to believe that the implementation of the repl is at fault here. However, before diving into the source, I wanted to probe some experts to see if there is some good reason for this behavior, or if it is just a stupid corner case that I've come up with.
Well, I don't know if it's kosher to answer my own question, but I dug into the nodejs source and found out what the deal is. I tracked it down to repl.js: line 404. Here there is a call to script.runInThisContext. At this point, the line throw new Error() "escapes" the repl and ends up getting caught a few lines below. Here, there is check for process.domain, which apparently succeeds in the repl, but not in a script context. This then emits a domain error, which doesn't do much. If you go to self._domain.on('error',... you will see that all the stack tracing stuff is taken care of here.
I actually added a line self._domain.emit('error'... to the source right before the process.domain.emit('error',... call, and it then printed out a nice stack trace for me, so I'll write the guys over at nodejs and see if it is something they would like to fix. There may be a very good reason not to do so, I didn't look into it in that great of detail.
At this point I'm reminded of a scene from Burn after Reading. Watch it if you have a dry sense of humor: https://www.youtube.com/watch?v=46h7oP9eiBk
Related
So I m getting an assertion error in postman, and the error looks like this:
There was an error in evaluating the test script: AssertionError: expected undefined to be a string
But i have 300 assertions for this API, and is virtually impossible to idenftify what assertion actually failed, is there a way for me to make postman show the line number that raised the assertionFail?
There is a way to get the line number if you dig down into this:
How do you find out the caller function in JavaScript?
Wrap your asserts in a very large try catch. Then print out the stack in the catch.
Details
Append the catch at the bottom. Something like this:
try {
// 300 asserts here
}catch (e) {
console.log(e);
console.log(`e.stack = ${e.stack}`);
}
When PM hits the assert you will see something like:
{type: "Error", name: "TypeError", message: "Cannot read property 'json' of undefined"}
e.stack = TypeError: Cannot read property 'json' of undefined
at Object.eval (eval at exec (evalmachine.<anonymous>:58:1931768), <anonymous>:89:30)
at u.exec (evalmachine.<anonymous>:58:1931803)
at t.exports (evalmachine.<anonymous>:58:5743)
at Object.<anonymous> (evalmachine.<anonymous>:58:7440)
at evalmachine.<anonymous>:15:26
at Array.forEach (<anonymous>)
at Object.emit (evalmachine.<anonymous>:14:54)
at evalmachine.<anonymous>:51:24
at evalmachine.<anonymous>:5:21
at evalmachine.<anonymous>:6:18
The top of the stack tells you the line number (zero-based):
at Object.eval (eval at exec (evalmachine.:58:1931768), :89:30)
So in this case 89, is reported as 88 in the IDE.
BTW it appears that the 30 is actually the column number of the called method.
Additional entries give a longer trace, showing the lineage of the calls before the error occurred, which may be useful in some debugging cases.
Warning this will cause the first assert error to interrupt any further testing. So undo this change when you are done.
I have a Kotlin/JS project that is throwing a null pointer exception, but I can't figure out where it is coming from. The source map in the browser references exceptionUtils.kt?24ab which is a generic wrapper for the javascript error:
#JsName("throwNPE")
internal fun throwNPE(message: String) {
throw NullPointerException(message)
}
The stack trace for the error shown in the javascript console is, likewise, referencing the javascript bridging code, not my actual Kotlin source:
"NullPointerException
at Object.captureStack (webpack-internal:///./kotlin-dce-dev/kotlin.js:38592:15)
at NullPointerException.Exception [as constructor] (webpack-internal:///./kotlin-dce-dev/kotlin.js:38925:14)
at NullPointerException.RuntimeException [as constructor] (webpack-internal:///./kotlin-dce-dev/kotlin.js:38951:17)
at RuntimeException_init_0 (webpack-internal:///./kotlin-dce-dev/kotlin.js:38962:24)
at new NullPointerException (webpack-internal:///./kotlin-dce-dev/kotlin.js:39071:7)
at Object.throwNPE (webpack-internal:///./kotlin-dce-dev/kotlin.js:42775:13)
at Kotlin.ensureNotNull (webpack-internal:///./kotlin-dce-dev/kotlin.js:748:35)
at main (webpack-internal:///./kotlin-dce-dev/my-app.js:68:27)
at Object.eval (webpack-internal:///./kotlin-dce-dev/my-app.js:431:3)
at eval (webpack-internal:///./kotlin-dce-dev/my-app.js:5:35)"
How can I trace this back to the actual Kotlin source for the exception?
I'm working on a discord bot that does a variety of things. It keeps randomly crashing and I tracked down the source of the error. What it does is it downloads images sent to it in a discord private message, hashes them and sets their name to the hash to make sure no duplicates exist. This is not the most efficient way to do so but it works.
Here's my code (it's a bit messy)
message.attachments.forEach(url => {
if (!message.author.bot) {
function getDate() {
let d = new Date().getTime();
return d;
}
tempdate = new getDate();
ext = path.extname(url.url);
request.get(url).on(`error`, console.error).pipe(fs.createWriteStream(`./temp/Img-${tempdate}${ext}`));
hash = sha256(`./temp/Img-${tempdate}${ext}`);
if (fs.existsSync(`./attachments/Img-${hash}${ext}`)) {
request.get(url).on(`error`, console.error).pipe(fs.createWriteStream(`./attachments/Img-${hash}${ext}`));
console.log(`Error: Duplicate File`)
}
fs.createWriteStream(`./attachments/Img-${hash}${ext}`);
console.log(`Wrote Hashed File.`)
fs.unlinkSync(`./temp/Img-${tempdate}${ext}`)
}
})
Every once in a while, however it returns this:
fs.js:1061
return binding.unlink(pathModule._makeLong(path));
^
Error: ENOENT: no such file or directory, unlink 'C:\Users\xxxxx\Desktop\testbot\temp\Img-[object Object].png'
at Object.fs.unlinkSync (fs.js:1061:18)
at Client.client.on.message (C:\Users\xxxxx\Desktop\testbot\yes.js:48:20)
at emitOne (events.js:116:13)
at Client.emit (events.js:211:7)
at MessageCreateHandler.handle (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\packets\handlers\MessageCreate.js:9:34)
at WebSocketPacketManager.handle (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\packets\WebSocketPacketManager.js:103:65)
at WebSocketConnection.onPacket (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:333:35)
at WebSocketConnection.onMessage (C:\Users\xxxxx\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:296:17)
at WebSocket.onMessage (C:\Users\xxxxx\node_modules\ws\lib\event-target.js:120:16)
at emitOne (events.js:116:13)
I assume I did something wrong here. I can't find any answers online
You are mixing synchronous and asynchronous code.
The .pipe() is absolutely asynchronous, yet you're trying to assume that it's done immediately. You can't reliably program this way.
You need to listen for an event to know when the .pipe() is done before you can proceed with the rest of the processing.
And, as soon as you make it event driven, your .forEach() will run ahead with multiple iterations trying to run at the same time and then the fact that your variables aren't declared locally inside the loop will cause variable conflicts.
Also, what is this line of code doing all by itself:
fs.createWriteStream(`./attachments/Img-${hash}${ext}`);
You create a write stream, but don't even keep track of it. That will just leak a file handle.
As for solutions, it really requires a significant rewrite. I've answered a similar question here Issue with request and .pipe(). In that answer, I created a promise wrapper for .pipe() which would then let you use await to sequence a pipe operation. I'd probably use that solution in rewriting this code.
Currently I'm trying to add to a blacklist array for my discord bot. Reading the array works fine, but whenever I try to append to the bot node exits with code 1.
I've tried a few ways, but none really seem to work.
const data = require('./data.json');
data.serverData.blackListedColors.append(randomRoleColor);
which returns:
data.serverData.blackListedColors.append(randomRoleColor);
^
TypeError: data.serverData.blackListedColors.append is not a function
at Client.<anonymous> (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\bot.js:363:59)
at Client.emit (events.js:209:13)
at MessageCreateHandler.handle (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\packets\handlers\MessageCreate.js:9:34)
at WebSocketPacketManager.handle (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\packets\WebSocketPacketManager.js:105:65)
at WebSocketConnection.onPacket (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:333:35)
at WebSocketConnection.onMessage (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:296:17)
at WebSocket.onMessage (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\node_modules\ws\lib\event-target.js:120:16)
at WebSocket.emit (events.js:209:13)
at Receiver.receiverOnMessage (C:\Users\mtutt\OneDrive\Documenten\DiscordKeyBot\node_modules\discord.js\node_modules\ws\lib\websocket.js:789:20)
at Receiver.emit (events.js:209:13)
Process finished with exit code 1
I've tried using .parse and .add but those also don't seem to work.
using .push doesn't seem to crash it, but also doesn't add the string to the .json file.
edit:
here's the .json file for reference
{
"serverData": {
"muffinID": "[disocrdID]",
"token": "[token]",
"blackListedColors": []
}
You can't use require() for data that changes during the lifetime of your bot process due to the Node require cache. In addition, just modifying data won't automagically persist it back into the file; you'll have to write it back into the file.
It's not rocket surgery to write yourself, but you may want to look into a library that does this for you – I haven't tried this, but it looks promising enough: https://www.npmjs.com/package/json-persistent-object
here's a snippet of my code
console.log("IS RESTCLIENT UNDEFINED?");
console.log(restClient === undefined);
console.log(restClient);
restClient.get(options, function(resterr, restreq, restres, restobj) {
assert.ifError(resterr);
//not important
return next();
});
The problem is that when it gets executed (it's basically most of the handler for a route, simple stuff, nothing esoteric) the output is as following:
IS RESTCLIENT UNDEFINED?
false
JsonClient {<a lot of stuff describing the client>}
assert.js:327
assert.ifError = function(err) { if (err) throw err; };
^
InternalError: restClient is not defined
at parseResponse (/srv/oms-profiles-module/node_modules/restify/lib/clients/json_client.js:67:23)
at IncomingMessage.done (/srv/oms-profiles-module/node_modules/restify/lib/clients/string_client.js:151:17)
at IncomingMessage.g (events.js:260:16)
at emitNone (events.js:72:20)
at IncomingMessage.emit (events.js:166:7)
at endReadableNT (_stream_readable.js:893:12)
at doNTCallback2 (node.js:430:9)
at process._tickCallback (node.js:344:17)
The client is defined at the beginning of the file "as usual" (worked until now), and it uses the npm module restify. I don't even understand if the problem could be the scoping or what else, has anybody gotten any good advice on where I could bang my head on?
Thanks
UPDATE: this is not a problem with scopes and such, I presume; I suspect it is something with restify because otherwise the error would be something like "ReferenceError: restClient is not defined". Still, I am at a loss since it was working before and I did not introduce any major change for it to break. Restify version is 3.0.3
Alright, the answer is simple and I feel stupid (as I should),
the assert was being fired as there is a resterr. It comes from the API handler that is being called with restClient.get()
However I was expecting the whole (other) program to do something like crash (on another assert) or to tell me ReferenceError: restClient is not defined, but analysing the logs it did NOT happen. Thus I thought that the problem was in the snippet up here and not in the remote API that I was calling (still coded by me).