-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exceptions in handlers getting swallowed up #74
Comments
Unfortunately it's not possible to throw exceptions from asynchronous callbacks in JavaScript. We are aware of the possibility of losing errors from inside callbacks, and we were experimenting with wrapping the callbacks in try/catches that would log these lost exceptions to stderr. I think this would be useful for debugging purposes, but this error log will not stop program flow, so it too can get lost in your output. Also, a library like aws-sdk should probably not try to take over your stderr pipe, so we would at least have to make this toggleable. As you allude to in #75, domains are one of node's proposed solution to this overarching technical limitation of the language. Domains are also one way we would like to support this kind of a problem, but the API is not stable enough for us to support fully yet-- more in that issue. |
Can you point to the section of code which is swallowing exceptions so I can disable it? The inability to see where errors propagate from is making development very difficult (think typos or reference errors). Note for others having this issue logging the |
Thanks a ton for fixing this - being able to catch exceptions at least at the run loop will make it much easier to debug issues. |
Where does this stand. Exceptions are still being swallowed as far as I can tell. It would be nice, for the little utilities that I'm writing, to just allow a thrown exception propagate up and kill the program. var AWS = require('aws-sdk')
AWS.config.loadFromPath(process.env.HOME + '/.aws')
AWS.config.update({region: 'us-east-1'})
new AWS.EC2().describeInstances(function () { throw new Error }) The above runs without writing any output. |
I am also seeing the behaviour described in the previous comment. Was this really fixed? |
Unfortunately this change had to be reverted due to #131, since this original change caused requests to be retried when errors were thrown from callbacks; not what you would want to happen. I'm currently looking into a way to get the best of both worlds. |
Heres some work that made me think of this issue. |
This bit me as well. I had if (err) { throw err; } in my callback, and I'm using process.on('uncaughtException') to log exception and end process. I think this is really surprising behaviour and this should be fixed. It is outrageous for a library to just swallow exceptions and hide them. |
@joonas-fi just to update-- this issue is fairly old. We actually did end up re-introducing logic to throw errors out of the callback, first in 3cdd424 (which is shown in this issue above) and then in a more elegant way with the release of v2.0.5 as @mhart pointed at in #307. In short, if you're on the latest version of the SDK your errors should be propagating out of the callback. If this is still happening to you, can you verify and report the version of the SDK you are using? |
├─┬ aws-sdk@2.0.17 Fresh install from npm a few days ago. I'm using s3.putObject() (edited:) $ node --version |
@joonas-fi do you have an example of code that swallows an error? |
This is somewhat what I'm doing:
And this is the output:
|
Ah, I can explain this. Errors will propagate, except if it happens to be the same error object the SDK gave you in the callback. Not saying that's how it should work, just that it's how it works right now: https://github.com/aws/aws-sdk-js/blob/master/lib/request.js#L29 For what it's worth, that check does seem a little odd. I just ran our tests with that That said, the following does throw, which you could use in the meantime: s3.putObject(params, function(err) { throw new Error(err) }); |
Oh okay, I kind of understand. Still that does not make much sense, since surprising behaviour is always surprising. As a user I wouldn't expect things to get handled differently if I just throw the original error object out, instead of throwing "custom" error object out. Good to know all tests still pass when the check is ripped out, sounds promising. Anyways, thanks for helping with this. I will use your trick of throw new Error(err) for now. I hope this gets fixed because I do think many people get bit by this. |
Why not do this inside try {
callback(error)
} catch (e) {
AWS._thrownByUser = e
throw e
} Later: try {
AWS._somethingThatWillCallTheBlockAbove()
} catch (e) {
if (e === AWS._thrownByUser) {
throw e
}
AWS._handleError(e)
} |
@bigeasy Storing the "last error" on the global object could potentially cause a memory leak if it wasn't properly cleaned up-- especially if at some point in the future we start referencing the related request object information in the error. More importantly, it's likely to cause async-based race conditions if a user launches two requests at once, or even does something in the callback that causes another AWS error to throw. I think the right solution here is to just drop that Thanks for your feedback! |
@lsegal None of that that you said would happen would happen. That's not how JavaScript works. |
@bigeasy there would certainly be an issue with memory leaks if the reference was not properly cleaned up. As for the race condition issue, perhaps we're using different terminology-- the issue here is that multiple sync / async operations writing to the same object can cause one of the callbacks to read the property too late. It depends on how this is all implemented, obviously, but the initially proposed solution above seems like it would be less error prone. Can you elaborate more on where exactly those two blocks would be in the SDK? Perhaps I'm not following on some of the details. |
There would be no async race condition because The leak is a non issue. You would only ever leak one exception. That would get all cleaned up when the exception unwound the stack to all the way to the event loop and the process crashed. If the program continued, that would exceptional and incorrect. If it did continue, you could simply set the reference to null the next time you called the user back.
But, again, recovering from an exception thrown through async calls is wrong. |
@bigeasy I'm not quite sure why you need a global property set instead of just setting it on the error (ie, In any case, I'm in agreement that the way |
@mhart Because I'd rather not alter an exception thrown by the user in any way. That's just me. It's not like a package scoped property (not global) it is any less safe or correct. |
Opened issue #392 to track this, @mhart, @bigeasy, @joonas-fi. Thanks for reporting and all the suggestions, they're all very helpful! |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread. |
This took me a while to figure out, I'm not sure where, but exceptions in (at least the success) handlers are being caught and not rethrown. Code like this:
// db is a configured dynamodb client object
db.client.query(query)
.on('error', db.onError)
.on('success', function(res) {
console.log("hi there");
throw "fun times";
}).send();
results in "hi there", but nothing else on the console, even if you set up a domain or an uncaught exception handler to print them out.
So most likely there's a try/catch somewhere that should rethrow (my guess).
The text was updated successfully, but these errors were encountered: