I’ve been getting a bunch of errors in this format over the past few months:
Exception in callback of async function: undefined
This obviously makes it very hard to debug as I have no idea where the error is coming from and it’s a large application with different tasks running in the background fairly often.
Any idea why it happens, and how I can get a more descriptive error message? This error seems to happen in a bunch of places across the app.
Having a quick look where this message comes from takes me here:
Which is logged out of the logError function.
If we look up, where that’s called, we see it’s used in Meteor.wrapAsync when a callback was not provided to the wrapped function on the client (line 108).
So I would look for places where you’ve used Meteor.wrapAsync on the client, and called the wrapped function without a callback.
If you are able to replicate this locally, you can set a break point in the meteor.js pacakge file where logErr is called and explore up the call stack to find the offending function
The odd part is that it should only log if err is truthy, but your log says undefined?
Okay, taking another look, I found this, which has that extra : on the end and runs on the server:
This time it’s in the definition of Meteor.bindEnvironment. So that’s the next thing to search for.
This one doesn’t check if an error was passed in before logging, which also matches the log you quoted. Just need to work out which function isn’t throwing an error!
If you want to put a breakpoint here and track down which function threw the error, it’s going to be a bit harder. The packaged code does end up in .meteor/local/build/programs/server/packages/meteor.js, so you can probably drop a debugger statement in there and run your app with the --inspect flag.
Alternatively, make your own stack trace by logging a new Error on that line
So I managed to reproduce it by throwing an error inside a callback of HTTP.get callback inside of a Meteor method call (although I feel like this happens for many sorts of errors in our system). Will try dig into deeper now
At least I know it’s setTimeout in this exception, although undefined message makes it pretty hard to track down if you don’t know where it’s coming from. The async function: undefined error is even worse.
hmmm, I’m think it just doesn’t like it when a method throws after it’s already returned to the client.
On the server, HTTP.get doesn’t need a callback, but if it’s a shared method, that’s not an option.
You could promisify the method and return a promise instead?
I am facing the same issue because I have try/catch blocks where I don’t want the error being thrown to interfere in the main thread. My hope with the code below is that the error would be printed in the console and still be caught by my error reporting tool
try {
throw new Error('FOO') // error that I don't want to block the main thread
} catch (e) {
console.log(e); // logging that works fine
Meteor.setTimeout(() => {
throw e; // error that is not handled as expected
});
}
Has anyone had any luck with this? We’ve seen the log message a lot recently on our server but can’t reproduce it locally so are a bit stumped as to how to find it. My hunch is that it emanates from the function dontBindEnvironment used by the Hook class.
Likely sources then might be DDP._reconnectHook and AccountsServer._validateLoginHook as they don’t specify debugPrintExceptions which would change the error printed. The reconnect hook is on the client though so that rules that out but we do have quite a lot of code added via Accounts.validateLoginAttempt . I’ve checked it though and can’t find anything async
I’m trying to investigate this, and I’m very confused! It’s definitely coming from bindEnvironment. The problem is in:
onException = function (error) {
Meteor._debug(
"Exception in " + description + ":",
error
);
}
Even though error is a Meteor.Error object, when it is received by Meteor._debug()as the 2nd argument it is undefined (I tried replacing Meteor._debug to log the type of each argument).
However, if it is received as the 1st argument it is logged correctly. Very weird! Any ideas why that is?
I have replaced Meteor._bindEnvironment() with my own identical function, except for this bit:
onException = function (error) {
Meteor._debug(
"Exception in " + description + ":",
error
);
if (typeof error === 'object') Meteor._debug(error); // <- I added this line
}
Now I get an error description and stack trace so I can track down the actual errors…
I would prefer to fix Meteor._debug so it can receive an Meteor.Error object as a 2nd parameter, but I don’t know why it’s not working
Edit: here’s a cleaner way of monkey-patching Meteor.bindEnvironment():
const defaultErrorHandler = function (error) {
Meteor._debug("Exception in callback of async function:", error);
if (typeof error === 'object') Meteor._debug(error);
}
const _origBindEnvironment = Meteor.bindEnvironment;
Meteor.bindEnvironment = function(func, onException, _this) {
return _origBindEnvironment.call(this, func, onException || defaultErrorHandler, _this);
}
Success!! I’ve tracked it down to (in my case) montiapm:agent which was imperfectly wrapping Meteor._debug(). Other APM agents based on Kadira might have the same problem. FYI @zodern…
Edit: btw @zodern, none of these errors were being logged in MontiAPM because alreadyTracked is erroneously set to true. I think the assumption that “We’ve changed ‘stack’ into an object at method and sub handers…” is not true when Meteor._debug() is called within Meteor.bindEnvironment().
Thanks @wildhart for finding the cause. I’ve checked the other common APM agents and they should be logging the error correctly. Tomorrow I will work on a fix for montiapm:agent.
btw @zodern, none of these errors were being logged in MontiAPM because alreadyTracked is erroneously set to true.
I think all of the apm agents have this problem. I will fix it tomorrow too in montiapm:agent.
To be fair, the code in Meteor._debug exists entirely to address difference between older browsers. Here’s a selection of the comments:
// IE Companion breaks otherwise
// IE10 PP4 requires at least one argument
// IE 9 doesn't have console.log.apply, it's not a real Object.
// Most browsers
// Chrome and Safari only hyperlink URLs to source files in first argument
// IE9
// IE8
And outside Meteor, if you have an exception in a callback to an async function, you don’t get any extra information