Error Handling in Node.js[source]
xml
<glacius:metadata> | |
<title>Error Handling in Node.js</title> | |
<description>Different methods of handling errors in Node.js</description> | |
<category>Legacy blog posts</category> | |
<category>Programming</category> | |
<category>JavaScript</category> | |
<category>Node.js</category> | |
</glacius:metadata> | |
<glacius:macro name="legacy blargh banner"> | |
<properties> | |
<originalUrl>https://tmont.com/blargh/2014/9/error-handling-in-nodejs</originalUrl> | |
<originalDate>2014-09-29T07:25:30.000Z</originalDate> | |
</properties> | |
</glacius:macro> | |
<p> | |
Error handling in Node is kind of weird. And by "kind of weird", I mean | |
"different than other languages". The semantics of which I shan't get into, | |
but it is my opinion that Node was designed… oddly. At least in terms of | |
error handling. There are several non-obvious ways in which an error can | |
bubble up or otherwise make your program crash, so here's a brief-ish rundown | |
of what to look out for. And how to not do things. | |
</p> | |
<h3><code>try</code>, <code>catch</code> and <code>throw</code></h3> | |
<p> | |
This is the easy one. Except for the parts where it's not easy. Those are hard. | |
</p> | |
<p> | |
The normal Java/C♯/C++/etc. way of handling errors is via the familiar | |
<code>try..catch</code> construct. It behaves like you would expect: | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
try { | |
var json = JSON.parse('nope'); | |
} catch (e) { | |
console.log(e); //[SyntaxError: Unexpected token o] | |
} | |
]]></glacius:code> | |
<p> | |
<code>JSON.parse</code> is a synchronous function, and therefore it <code>throw</code>s | |
errors. That's important to remember. You should <strong>never, never, never, ever throw | |
an exception from asynchronous code. Ever. Srsly.</strong> | |
</p> | |
<h3><code>error</code> events</h3> | |
<p> | |
<code>error</code> events are kind of stupid. They are a "special" event within Node, and they're | |
"special" behavior is that if an <code>error</code> event is emitted without any | |
listeners, it crashes the program. Let me repeat that, because repeating things | |
is something you do when writing to help emphasize something. Or so my high school | |
English teachers taught me. | |
</p> | |
<p class="text-center"> | |
<strong><code>error</code> events without listeners crash programs.</strong> | |
</p> | |
<p> | |
There are no exceptions to this rule, and you can't get around it. Except by listening for | |
<code>error</code> event. They are very similar to checked exceptions in Java, so those | |
of you who sold your soul to the <code>AbstractStrategyFactoryFactory</code> can rest | |
easy, as this paradigm should be familiar to you already. | |
</p> | |
<p> | |
Now, normally, this isn't a huge problem, as it's | |
<a href="https://nodejs.org/api/events.html#error-events">documented</a> | |
and standardized, so if you care about something emitting an error and want it to not | |
crash your program, you can add a listener and handle it yourself. But it's not | |
so easy. | |
</p> | |
<p> | |
It's not easy because it's turtles all the way down. Many userland libraries | |
often do some sort of networking, e.g. connecting to a database. This means | |
it has to open a socket. Usually that's all abstracted so that the library is | |
easy to use. Often it's the reason for the library. Abstractions are supposed | |
to make things easy (and why <code>FactoryFactory</code>s seem to be so | |
abundant). | |
</p> | |
<p> | |
So, now you're connecting to your super awesome NoSQL CouchiakooseandradisDB | |
database using some random dude's library, which happens to abstract a socket | |
connection. But is that random dude's library handling the | |
<a href="https://nodejs.org/api/events.html#error-events"><code>error</code> | |
event</a>? If not, you'll need to dig through that dude's probably well-tested | |
and intelligently-factored code to figure it out. And if it isn't handled, | |
then you'll have to figure out how to get a hold of the underlying socket | |
connection and handle the error yourself. Which kind of defeats the purpose | |
of an abstraction. I assure you this has happened to me before. It's rather | |
annoying. | |
</p> | |
<p> | |
The point is, <code>error</code> events can bite you in the ass, because | |
they're hard to pinpoint, the stack trace isn't always useful and they | |
crash your program for seemingly no reason. Be careful and be aware. | |
</p> | |
<h3>Asynchronous errors</h3> | |
<p> | |
Now the real fun. Node is all about those non-blocking system calls. It's | |
what makes it semi-awesome. Or, at the very least, it's what makes it | |
different. Depending on how angry you are at random dudes, it might | |
be the worst thing since the <code>goto</code> statement. | |
</p> | |
<p> | |
First, let's distinguish between "asynchronous" and "functions that | |
return stuff via callback". <strong>Asynchronous</strong> means it's | |
literally not blocking execution. When you call an asynchronous function, | |
it's going off to do something and meanwhile, the rest of your program | |
is executing until that other thing finishes. The act of passing around | |
a callback function does not mean that it is asynchronous. If you can | |
remember this fact, you'll have a leg up on many random dudes. But not | |
in that way, pervert. | |
</p> | |
<p> | |
The Node convention for passing errors/results in asynchronous functions | |
is to pass a callback function as the last argument which takes two | |
arguments: an <code>err</code> and a <code>result</code>. The <code>err</code> | |
is an error, and if it's truthy, that means something broke. | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
fs.stat('/nonexistent/file', function(err, stat) { | |
if (err) { | |
console.error(err); | |
return; | |
} | |
// do something with the stat object | |
}); | |
]]></glacius:code> | |
<p> | |
And that's pretty much it. Always remember to handle your errors. Oftentimes | |
the <code>result</code> will be <code>null</code> or simply undefined if an error | |
occurs. Oh yeah. One other thing. | |
<strong style="text-transform: uppercase">Don't ever mix <code>try..catch</code> | |
idioms with callback idioms</strong>. I'll hate you forever if you do. And it's | |
bad practice and very confusing, and your stack trace will not be what you think | |
it should be. | |
</p> | |
<p> | |
The only time an asynchronous function (or one that returns its result via a callback) | |
should throw an error is if the arguments are bad. Those are programmer errors (e.g. | |
compile-time errors) and should be fixed by a programmer. For example, if a function | |
expects an object but you pass it a string, it's okay if it throws an exception in | |
that case. | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
function asyncSomething(someObject, callback) { | |
// okay to throw an exception in this case, although just passing back an error | |
// in the callback is totally reasonable as well | |
if (typeof(someObject) !== 'object') { | |
throw new Error('First argument should be an object'); | |
} | |
} | |
]]></glacius:code> | |
<h3>A seemingly common error</h3> | |
<p> | |
I've noticed that several well-known and well-used libraries (node-redis and | |
mongoose, specifically) fall into this trap of poor error handling. There are | |
probably others as well, but those are the two where I've noticed this. | |
</p> | |
<p> | |
Basically, they have a function definition like so: | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
function doSomething(callback) { | |
//do something asynchronously, and then... | |
try { | |
callback(); | |
} catch (e) { | |
this.emit('error', e); | |
} | |
} | |
]]></glacius:code> | |
<p> | |
Can you spot the problem? | |
</p> | |
<p> | |
The problem is that it's attempting to <code>catch</code> an error | |
that occurs <em>outside of its scope</em>, and then captures that | |
error and emits it as if it was an error that occurred inside of | |
the library. The following code sample should demonstrate why that is | |
bad: | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
doSomething(function(err) { | |
// ReferenceError: foo is not defined, i.e. programmer error | |
// this should crash the program, as it needs to fixed by a programmer | |
foo; | |
}); | |
]]></glacius:code> | |
<p> | |
What happens in this situation? Suppose the library that has the | |
<code>doSomething</code> function was not super important to your | |
application, and you don't really care if it <code>error</code>'d. So that means | |
you'll be handling the <code>error</code> event, logging it, and | |
ignoring it. What happens when the above code is executed? Will the | |
program crash as it should? | |
</p> | |
<p> | |
The answer is a quizzical "No". <code>doSomething</code> will catch | |
the undefined error and "re-throw" it by emitting it as an <code>error</code> | |
event. Since you are listening for those error events and ignoring them, | |
your program will carry on as if nothing bad happened. But in fact, | |
something terrible happened. This is an uncaught exception from the | |
runtime's point of view, and here is what the | |
<a href="https://nodejs.org/api/process.html#process_event_uncaughtexception">Node | |
docs</a> have to say about those: | |
</p> | |
<blockquote><p> | |
An unhandled exception means your application - and by extension node.js itself - | |
is in an undefined state. Blindly resuming means anything could happen. | |
</p></blockquote> | |
<p> | |
By catching every random error and emitting it as if it originated from itself, | |
the <code>doSomething</code> function is potentially placing Node itself into | |
an undefined state. This is bad. | |
</p> | |
<p> | |
You might be tempted to say "But why don't you just add an error event listener | |
and crash the program yourself?" The answer is because that's a super brittle | |
way of handling errors, e.g. doing a regex test on the error message looking for | |
"SyntaxError". | |
</p> | |
<p> | |
The point is that a library should never, ever capture an error that did not originate | |
from itself. I actually discovered this by accidentaly spelling a variable wrong | |
and being extremely confused when the stack trace implied that it originated | |
from Redis, when in fact it was just me being an idiot. I was even more confused | |
when my program didn't crash. Redis was not abundantly important to my application, | |
so if it was unavailable for whatever reason (e.g. connection issues) I didn't really care | |
and wanted things to keep running. So I had an <code>error</code> event listener | |
on Redis which logged the error and let the program keep on running. So imagine my surprise | |
when a legitimate syntax error didn't crash the program as it should have. | |
</p> | |
<p> | |
In conclusion, always handle your errors. But don't handle errors that don't | |
belong to you. | |
</p> | |