Error handling: current state of affairs?

471 views
Skip to first unread message

Tony Mobily

unread,
Jul 6, 2013, 7:17:28 AM7/6/13
to nod...@googlegroups.com
Hi,

I have been writing a bit of code with nodejs, and am sort of "going back" to brush things up, checking that I am doing things the right way.
I recently read this:


After a bit of research, I got to this article:


And to this SO answer:


At the moment, I am only ever throwing() if:

1) I am enclosing _async_ code with try/catch, like this:

 // Get the messages from Json, safely
  try {
    if( ! req.body.messages )
      throw( new Error("req.body.messages not there") );
    var messages = JSON.parse(req.body.messages);
  } catch(e) {
    var messages = [];
  }

2) Something reeeeeeeeaaaaaaaalllllllyyyyyyy bad happens in terms of how my module was used. For example a class constructor is missing a necessary parameter, etc.

In any other case, I am using next( err ). If something really bad happens, for example mongodb dies and calls to the db start failing, I handle it with an error manager in express:

app.use(  function( err, req, res, next){
 // ...
});

But... does this mean that if my application uses a library that has a random throw(), my app will effectively die?
What's the "current" state of affairs?

Looking at existing code, well, I seem to have gotten it right: nodejs libraries tend to only throw when things really aren't supposed to happen. For example in qs/lib/querystring.js:

function stringifyString(str, prefix) {
  if (!prefix) throw new TypeError('stringify expects an object');
  return prefix + '=' + encodeURIComponent(str);
}

But... am I missing something?
Would this be correct:

* throw() when the program really deserves to die, and not for external causes (see: the db server goes down, etc.)
* Always use next( err ) if anything goes wrong (business as usual)
* Figure out if some libraries emit events, and listen to them if necessary

Bye,

Merc.

Forrest L Norvell

unread,
Jul 6, 2013, 12:42:04 PM7/6/13
to nod...@googlegroups.com
My suggestion would be to use domains. Careful use of domains should obviate the need for try/catch altogether, and will handle async errors in Express much better than the default error handler.

Getting Express / Connect up and running with domains is a little tricky, but if you check out the NodeConf slides (https://othiym23.github.io/nodeconf2013-domains/#/), they have some example middleware functions that show you what's up.

Forrest
--
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en
 
---
You received this message because you are subscribed to the Google Groups "nodejs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nodejs+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Tony Mobily

unread,
Jul 6, 2013, 12:55:02 PM7/6/13
to nod...@googlegroups.com
Hi,

I saw it. It's good stuff -- thanks!

Merc.


You received this message because you are subscribed to a topic in the Google Groups "nodejs" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/nodejs/ZfaJPYyHKoA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to nodejs+un...@googlegroups.com.

joel

unread,
Jul 8, 2013, 1:01:41 AM7/8/13
to nod...@googlegroups.com
I started working a project that demonstrate domains both on vanilla node and with express (it's based on the nodeconf session).
take a look and feel free to send pull requests.

from some reason the /database route does not get caught by the domain -  https://github.com/oren/domains-examples/blob/master/express/server.js#L35
any idea what's missing there?

joel

unread,
Jul 8, 2013, 3:21:57 AM7/8/13
to nod...@googlegroups.com
sorry, the /database route is actually being caught. it's the /throw route that is not.

Martin Cooper

unread,
Jul 8, 2013, 12:17:11 PM7/8/13
to nod...@googlegroups.com
On Sun, Jul 7, 2013 at 10:01 PM, joel <oren...@gmail.com> wrote:
I started working a project that demonstrate domains both on vanilla node and with express (it's based on the nodeconf session).
take a look and feel free to send pull requests.

from some reason the /database route does not get caught by the domain -  https://github.com/oren/domains-examples/blob/master/express/server.js#L35
any idea what's missing there?

The problem is with Express. It wraps a try / catch around your request handler function, such that any synchronous exception (e.g. from your '/throw' handler) will be caught by that, and not handled by the domain. The reason your '/database' handler works is that the exception is thrown outside of the request handler itself, because of that setTimeout() call.

--
Martin Cooper
 

--

Adam Crabtree

unread,
Jul 8, 2013, 1:38:38 PM7/8/13
to nod...@googlegroups.com
The short answer is to use domains to help you die gracefully, but an uncaughtException or a domain error REQUIRES a restart of the process.

The long answer is use trycatch, https://github.com/crabdude/trycatch. Because domains are not the equivalent of node.js' "On Error Resume Next" (See http://nodejs.org/docs/latest/api/all.html#all_warning_don_t_ignore_errors), there are certain obvious failure cases that resuming on error will place your code into a bad state (e.g., EventEmitter handler errors: https://github.com/joyent/node/issues/5114). trycatch resolves these by shimming EventEmitter and wrapping all handlers in try/catch, moving much closer to "On Error Resume Next". It also doesn't catch errors that have torn multiple layers of core stack (or to put it another way, only catches core errors that are TypeErrors occurring at the first layer of core logic).

I'm currently wrapping up a comprehensive blog post on node.js error handling. I'll followup here with a link when it's posted.

Cheers,
Adam Crabtree
--
Better a little with righteousness
       than much gain with injustice.
Proverbs 16:8

Adam Crabtree

unread,
Jul 8, 2013, 2:40:17 PM7/8/13
to nod...@googlegroups.com
FWIW, another common gotcha WRT domains (that trycatch addresses) is that since domains don't "catch" errors, code after a domain.run does not run when a run throws. E.g.,

var d = require('domain').create();
d.on('error', function(err){
    console.log('here');
});

d.run(function(){
    throw new Error('example');
});
console.log('this does not happen')

vs...

trycatch(function() {
  throw new Error('example');
}, function(err) {
    console.log('here');
})

console.log('this happens')

The reason this is important is due to a concept known as stack tearing. Essentially, you don't know what code is *meant* to / would happen after the error, so resuming on error means you will be in an undefined state. try/catch solves this, but domains don't use try/catch, they use a special form of process.on('uncaughtException'). I'll address all this further in my post, but as I was reviewing your very good references (specifically http://stackoverflow.com/questions/7310521/node-js-best-practice-exception-handling) it came to mind since there's more happening than meets the eye.

Cheers,
Adam Crabtree

Benjamin Zuill-Smith

unread,
Oct 26, 2013, 2:27:04 AM10/26/13
to nod...@googlegroups.com
Would others agree from what has been discussed in this thread that the best approach is to use domains and try/catch? I am looking to prevent my express server from erroring out. It seems like  try/catch will allow a domain to continue running and thus I can still respond to a user request, but if I miss something then domains would prevent the server process from shutting down and I only lose the request. Does this sounds about right?


On Saturday, July 6, 2013 4:17:28 AM UTC-7, Tony Mobily wrote:

Alex Kocharin

unread,
Oct 26, 2013, 7:22:34 AM10/26/13
to nod...@googlegroups.com

There's no such thing as "best approach". It's a decision a developer has to make, and the decision should be based on a particular reasons, not on other people's opinions about what the best is.

As far as I can see, the most common approach right now is just trying to catch all common errors manually and pass it to callbacks (maybe using flow control library like async). If something unexpected happens, process will just crash and be restarted, but it is expected to happen rarely enough.

I had high hopes for the domains api, but it looks like this idea is either failed or has a very niche use, because I don't really remember any projects that use it.

Another interesting idea will be to use synchronous api and generators, but it isn't even possible by default in unstable yet.

Bruno Jouhier

unread,
Oct 26, 2013, 10:26:03 AM10/26/13
to nod...@googlegroups.com
You will have difficulties getting an agreement on a "best approach".

If you want something simple and robust, you may want to take a look the tools that let you write code in sync style. Most of them (all the ones based on fibers and generators, some of the CPS transforms) give you try/catch and try/finally that work across async calls. Then, you can use good old exception handling recipes.

I'm the author of one of these tools and one of my main motivations was to have simple and robust exception handling. I did not want our servers to go down at the first uncaught exception. Instead I wanted our APIs to just log uncaught exceptions and return a 500. This became easy again once we had try/catch working across async calls.

Bruno

spion

unread,
Oct 26, 2013, 3:16:35 PM10/26/13
to nod...@googlegroups.com
I'll mention Promises, which give you async versions of try/catch/finally[1] and:

- don't require code transformation
- don't require new language features
- don't require domains or native code (so they work in all browsers too)
- provide an easy way to manage/dispose of resources [2] (which means process shutdown isn't mandatory)
- come with control flow powers [3]
- support dual promise/callback style API [4]
- have optional support for long stack traces [5]
- will become a part of EcmaScript 6 (Harmony)
- will interoperate well with generators
- are widely used in many popular client-side libraries



Peter Rust

unread,
Oct 27, 2013, 10:30:14 AM10/27/13
to nod...@googlegroups.com
Benjamin,

You may find TJ Holowaychuk's Koa.js interesting -- middleware using generators. Apparently there are big performance benefits and you can catch both sync and async errors with try/catch... but as others have said, it's experimental: you have to be on the latest (unstable) release of node and you have to enable generators in v8 with a command-line flag.

You've probably read the official warning about using domains as a way to ignore errors, but I figured I'd post a link just in case.

Regarding domains, in The Future of Programming in Node.js, isaacs said:

> Domains will be refactored to support more generic 
> continuation-tracking systems, to enable alternative error-handling 
> mechanisms in userland.  Eventually the Domain module will be a thing 
> that could be done in userland, but it will continue to be bundled 
> with the Node binary.

Personally, I'm excited about this low-level hook because it should make it possible to implement automatic long-stack traces without hacks (though this is probably something you would want to have only in debug mode due to the performance implications). It might even make it possible to avoid littering the code with "if (err) return cb(err)" (or d.intercept) at ever layer of asynchrony -- that would be quite nice.

Forrest L Norvell

unread,
Oct 27, 2013, 4:42:48 PM10/27/13
to nod...@googlegroups.com
I had high hopes for the domains api, but it looks like this idea is either failed or has a very niche use, because I don't really remember any projects that use it. 

Domains are doing just fine. It's mostly an end-use API for use by application developers, but support for it is gradually showing up in the places where modules need a little extra shimming to work properly. The node-redis module now has support for domains, and hapi uses domains directly for its own error-handling. It's also pretty much a drop-in to write a middleware that works with Express or Restify to put all your middleware and route handlers into domains.

I'll mention Promises, which give you async versions of try/catch/finally[1] and…

Handling errors with promises is simple and clear (I find it one of the most pleasant parts of working with promises), but until the advent of bluebird, they incurred a pretty substantial performance penalty, if the main reason you were using promises was for error-handling. Having all those try clauses is expensive. And if you look at what bluebird has to do to get around that, it's kind of hairy and not all that different from what the domains system was doing under the hood.

Personally, I'm excited about this low-level hook because it should make it possible to implement automatic long-stack traces without hacks (though this is probably something you would want to have only in debug mode due to the performance implications). It might even make it possible to avoid littering the code with "if (err) return cb(err)" (or d.intercept) at ever layer of asynchrony -- that would be quite nice.

You should take a look at https://github.com/joyent/node/pull/6011, which is the API to which Isaac is alluding. It's ready to be landed on node master pending review, and has the error-handling hooks you describe (in fact, in #6011, domains have been reimplemented in terms of the asyncListener API). I've been working with this API for a month or two now (I maintain the polyfill that brings its functionality back into 0.10 and 0.8: https://github.com/othiym23/async-listener), and while I think it offers a lot of improvements in terms of capturing information about errors, it's not a full replacement for traditional Node error-handling. In particular, you still need something like domains to keep the error-handling happening as close to the scope of where the errors are signaled as possible.

And yes, the "Hello, World" of the asyncListener API is probably the long stacktrace module. Trevor Norris (the implementor of #6011) wrote one to debug the API, and a bunch of other people have done so as well. You're right -- generating all those stacktraces is expensive!

F



--

Peter Rust

unread,
Oct 27, 2013, 4:52:54 PM10/27/13
to nod...@googlegroups.com
Thanks for the pointers, Forrest, I'll definitely take a look! And thanks for the update on framework/middleware support for domains.

tjholowaychuk

unread,
Oct 27, 2013, 11:22:33 PM10/27/13
to nod...@googlegroups.com
FWIW co/koa still don't entirely solve the problem, there's really no way to completely solve it without coroutines with separate stacks, but co mitigates it quite a bit, you'd normally have to inline 4-5 try/catches if you REALLY wanted to be robust but co() effectively does that for you

Bruno Jouhier

unread,
Oct 28, 2013, 3:35:46 AM10/28/13
to nod...@googlegroups.com
FWIW co/koa still don't entirely solve the problem, there's really no way to completely solve it without coroutines with separate stacks,

No! Problem can be solved with generators alone. Of course it is easier with real coroutines (fibers) but it is also manageable with shallow continuations. Take a look at https://github.com/bjouhier/galaxy.

Try/catch semantics can also be implemented with pure callbacks (try/finally too) but the pattern is very heavy. It's only viable if you preprocess the code.

Bruno

Alexey Petrushin

unread,
Oct 28, 2013, 4:51:22 PM10/28/13
to nod...@googlegroups.com
I use fibers and forget about async errors problems.

spion

unread,
Oct 28, 2013, 7:31:58 PM10/28/13
to nod...@googlegroups.com

I'll mention Promises, which give you async versions of try/catch/finally[1] and…

Handling errors with promises is simple and clear (I find it one of the most pleasant parts of working with promises), but until the advent of bluebird, they incurred a pretty substantial performance penalty, if the main reason you were using promises was for error-handling. Having all those try clauses is expensive. And if you look at what bluebird has to do to get around that, it's kind of hairy and not all that different from what the domains system was doing under the hood.

True. The end result however is an API that is simple and straightforward to use - arguably even easier than domains.
 

Personally, I'm excited about this low-level hook because it should make it possible to implement automatic long-stack traces without hacks (though this is probably something you would want to have only in debug mode due to the performance implications). It might even make it possible to avoid littering the code with "if (err) return cb(err)" (or d.intercept) at ever layer of asynchrony -- that would be quite nice.

You should take a look at https://github.com/joyent/node/pull/6011, which is the API to which Isaac is alluding. It's ready to be landed on node master pending review, and has the error-handling hooks you describe (in fact, in #6011, domains have been reimplemented in terms of the asyncListener API). I've been working with this API for a month or two now (I maintain the polyfill that brings its functionality back into 0.10 and 0.8: https://github.com/othiym23/async-listener), and while I think it offers a lot of improvements in terms of capturing information about errors, it's not a full replacement for traditional Node error-handling. In particular, you still need something like domains to keep the error-handling happening as close to the scope of where the errors are signaled as possible.

And yes, the "Hello, World" of the asyncListener API is probably the long stacktrace module. Trevor Norris (the implementor of #6011) wrote one to debug the API, and a bunch of other people have done so as well. You're right -- generating all those stacktraces is expensive!


Trevor Norris

unread,
Oct 29, 2013, 2:19:11 AM10/29/13
to nod...@googlegroups.com
On Sunday, October 27, 2013 1:42:48 PM UTC-7, Forrest L Norvell wrote:
Personally, I'm excited about this low-level hook because it should make it possible to implement automatic long-stack traces without hacks (though this is probably something you would want to have only in debug mode due to the performance implications). It might even make it possible to avoid littering the code with "if (err) return cb(err)" (or d.intercept) at ever layer of asynchrony -- that would be quite nice.

You should take a look at https://github.com/joyent/node/pull/6011, which is the API to which Isaac is alluding. It's ready to be landed on node master pending review, and has the error-handling hooks you describe (in fact, in #6011, domains have been reimplemented in terms of the asyncListener API). I've been working with this API for a month or two now (I maintain the polyfill that brings its functionality back into 0.10 and 0.8: https://github.com/othiym23/async-listener), and while I think it offers a lot of improvements in terms of capturing information about errors, it's not a full replacement for traditional Node error-handling. In particular, you still need something like domains to keep the error-handling happening as close to the scope of where the errors are signaled as possible.

Thanks Forrest. Guess I should jump in on this being my PR and all (though huge props to the New Relic team helping me test/debug the sucker).

So, here's and example script I threw together (completely unoptimized) using the new AsyncListener API to create long stack traces: https://gist.github.com/trevnorris/7209654

As far as performance, I dunno what people are expecting. I ran the script in benchmark/http_simple.js and ran wrk directly against it. Here are the results of a couple different runs:

wrk -c 32 -t 4 -d 10 'http://127.0.0.1:8000/bytes/1024'

Normal: 15,000 req/sec @ 47MB
Long Stack: 6500 req/sec @ 55MB

wrk -c 32 -t 4 -d 10 'http://127.0.0.1:8000/bytes/1024/4'

Normal: 800 req/sec @ 25MB
Long Stack: 775 req/sec @ 55MB

As far as what you can get from it, there's no other library that can give you as in depth information as you can get from the AsyncListener API. It runs for _every_ asynchronous event, including the internal ones you're not alerted about. You're also given access to the request context so it can be inspected at error time. The request context meaning the object that wraps the C++ request in process. So technically a native module could be written to inspect all the classes in the call stack as well.

Let's definitively clarify something. Domains were mostly layered onto existing code. AsyncListeners are fundamental to how Node operates. It was the only way to include the feature w/o introducing performance impact when not in use (unlike domains). There is nothing asynchronous in core that doesn't use the AsyncWrap class, thus giving you an easy hook into all asynchronous events. So, honestly, i'm interested to see how the community can push the limits of this new feature.

Trevor Norris

unread,
Oct 29, 2013, 2:22:28 AM10/29/13
to nod...@googlegroups.com
On Monday, October 28, 2013 4:31:58 PM UTC-7, spion wrote:
Handling errors with promises is simple and clear (I find it one of the most pleasant parts of working with promises), but until the advent of bluebird, they incurred a pretty substantial performance penalty, if the main reason you were using promises was for error-handling. Having all those try clauses is expensive. And if you look at what bluebird has to do to get around that, it's kind of hairy and not all that different from what the domains system was doing under the hood.

True. The end result however is an API that is simple and straightforward to use - arguably even easier than domains.

Though I don't think easier than just running the following in existing code:

require('./longstack').start();

;)

(example script for reference: https://gist.github.com/trevnorris/7209654

Floby

unread,
Oct 29, 2013, 6:31:30 AM10/29/13
to nod...@googlegroups.com
Why is nobody prescribing using TDD ? :)
Reply all
Reply to author
Forward
0 new messages