JavaScript error handling anti-pattern
One of the areas I feel lacks enough discussion is error handling in JavaScript. While a lot of thought typically goes into error handling in server software, complete with error logs and monitoring systems, there’s very little emphasis on the same for JavaScript. I’ve tried to raise awareness of this with my Ajax Experience talk, Enterprise JavaScript Error Handling, where I discussed error handling approaches as well as common error sources.
One of my suggestions in the talk was to provide a debug mode for your application. The idea is that production mode hides JavaScript errors from the user and handles them appropriately while debug mode allows errors to bubble up to the browser level and be reported as usual. The latter is important, of course, for debugging purposes. When the error is popped up in the browser, you have the option to debug with all of the surrounding context information. The pattern I suggested in my talk looks like this:
function doSomething(value){
try {
process(value);
} catch (ex){
if (debugMode){
throw ex;
} else {
log(1, "doSomething(): " + ex.message);
}
}
}
The idea here is that the error is caught and, depending on the mode, does something appropriate. As often happens in development, I’ve now discovered that this isn’t the best approach and actually introduces a painful consequence.
If an error occurs in process(), that error is trapped and thrown from doSomething(), which disrupts the call stack. The error is now too far away from the actual event to be useful for debugging. All of the context information that could lead to a solution is lost once execution exits process(). Imagine having your debugger set to break on all errors: using this code, the break would occur on the line containing throw ex when you really want it to break inside of process() because that’s where the actual problem is.
I now consider this an error handling anti-pattern, a pattern that prevents useful debugging rather than enabling it. The pattern I now recommend is to completely remove the try-catch statement when in debug mode. This allows for normal code execution and will result in the correct call stack placement when an error occurs. There are a couple of ways to accomplish this pattern, the first is a rather ugly-looking conditional statement:
function doSomething(value){
if (debugMode){
process(value);
} else {
try {
process(value);
} catch (ex){
log(1, "doSomething(): " + ex.message);
}
}
}
The second, arguably more elegant approach is to simply replace the entire function based on the execution mode:
var doSomething = debugMode ?
function(value){
process(value);
} :
function(value){
try {
process(value);
} catch (ex){
log(1, "doSomething(): " + ex.message);
}
};
This is my preferred approach because it eliminates checking debugMode each time the function is executed. Also, this approach is easy to automate. Suppose you have one or more objects and you want all of their methods to have a wrapper in production to trap errors. The following code accomplishes this quite easily:
//by Nicholas C. Zakas (MIT Licensed)
function productionize(object){
var name,
method;
for (name in object){
method = object[name];
if (typeof method == "function"){
object[name] = function(name, method){
return function(){
try {
return method.apply(this, arguments);
} catch (ex) {
log(1, name + "(): " + ex.message);
}
};
}(name, method);
}
}
}
This code iterates over an object’s properties and replaces each function with another function containing the appropriate error handling mechanism. You can use the function like this:
var system = {
fail: function(){
throw new Error("Oops!");
}
};
function log(severity, message){
alert(severity + ":" + message);
}
if (!debugMode){
productionize(system);
}
system.fail(); //error is trapped!
This pattern of error trapping will serve you well in complex environments where errors can be difficult to track down. Making sure that the error is thrown from the right place is the first step in debugging the problem.
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of my employer, my colleagues, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
Both comments and pings are currently closed.




19 Comments
Hi Nicholas,
Great article – I definitely like your approach of walking over an object and “productionizing” it. I have written previously about the underlying technique – that is, replacing a given function with a wrapper which performs additional logic. I’d be interested in your feedback.
– Jim
Jim R. Wilson on April 28th, 2009 at 5:21 pm
I use a similar scheme to trap front-end errors, hiding them from users while reporting them to the devs. It’s a bit more advanced than what’s shown here, as it lets us track front-end javascript errors remotely by sending the error info back to our server. Most every function in our application is wrapped by a try{}catch(e){} and the debug code inside the catch can either report immediately to a dev (if the app is being executed on a dev machine), or send a silent report via http to our server if the user is not a dev. We get back the javascript error, the function caller, and if we really need specifics we can track the line within the function that is causing the error. It has been working out nicely for us.
neimado on April 28th, 2009 at 5:23 pm
The anti pattern or the two kinds of error handling can be generated on server side also. I mean the try…catch block can be easily inserted by PHP in non-debug mode.
(neimado, I think that the “log” in the example means a silent report sending via HTTP)
zrek on May 4th, 2009 at 5:03 pm
@zrek – I disagree that the try-catch can easily be inserted via PHP. That may work if you’re willing to put your JavaScript in PHP, but I prefer to keep them separate. In my opinion, it’s easier and safer to programmatically add this using JavaScript.
Nicholas C. Zakas on May 6th, 2009 at 12:56 am
I disagree with this method of debugging in general. All the example above introduce considerable performance overhead; essentially, at minimum, an evaluation for every method call.
In my opinion, debug code should never even be present in production-release code. A slightly better way in my opinion would be to have a seperate debug.js file which interates through all the public methods and wraps them in the debug function wrapper like above. This way you could just leave it out.
The ideal method would be to use assertion-style debugging and prefix these lines in a special way e.g.
and as a part of your release/tagging process, have all of your Javascript files parsed to strip all debug lines.
I particularly like this method, and you can create a simple and useful script which takes a switch for development/staging/production and will perform different tasks depending. At the same time it can minify or pack your code as well. I think this is the best way to handle source packaging between different environments.
Anthony J. Lucas on May 23rd, 2009 at 1:56 pm
Just following on from my previous comment, I wanted to clarify that I’m in favour of more useful debug reporting but am not a fan of hiding errors in production code. I think people should handle the various possible states gracefully. try and catch in Javascript are pretty useless you do not control the code which is throwing the exception, because if you did you could just have it return null or some other graceful behaviour. In that way, exceptions should never be thrown with the intention of catching them, because they’re simply an overhead (in maintenance and performance) in comparison to returning null.
Anthony J. Lucas on May 23rd, 2009 at 2:15 pm
@Anthony – I think you’re confusing debugging with error handling. The purpose of my code is to provide generalized error handling in a production environment, in which you wouldn’t want regular JavaScript errors to show up. In my mind, it’s a cardinal rule that your users should never see a JavaScript error being reported by the browser, which is where this code comes in useful.
It also sounds like you’re concerned about the performance of this approach. In my experience, the performance impact is quite minimal especially when compared with the improved user experience and error reporting that it enables. Combining this technique with logging errors back to the server gives you unmatched insights into what JavaScript errors are occurring in production.
While I do agree that you shouldn’t release code with known errors occurring, there’s always a situation that you didn’t account for or an error that may occur in certain circumstances only. In those cases, this technique helps you to preserve the user experience.
Nicholas C. Zakas on May 23rd, 2009 at 11:18 pm
I use a similar scheme to trap front-end errors, hiding them from users while reporting them to the devs. It’s a bit more advanced than what’s shown here, as it lets us track front-end javascript errors remotely by sending the error info back to our server. Most every function in our application is wrapped by a try{}catch(e){} and the debug code inside the catch can either report immediately to a dev (if the app is being executed on a dev machine), or send a silent report via http to our server if the user is not a dev. We get back the javascript error, the function caller, and if we really need specifics we can track the line within the function that is causing the error. It has been working out nicely for us.
PB on May 27th, 2009 at 4:51 am
Very handy! I’ll be using/testing this technique soon!
Sam Vloeberghs on June 15th, 2009 at 3:30 am
[...] The implementation of observeMethod is a variation of a technique first documented by Nicholas Zakas: [...]
qooxdoo » News » Global Error Handling on June 19th, 2009 at 11:33 am
What do you think of log4javascript, or any other js logging framework for that matter?
Paul A. Boivin on January 26th, 2010 at 9:38 am
@Paul – log4javascript is fine, most logging utilities are about the same, as far as I’m concerned.
Nicholas C. Zakas on January 26th, 2010 at 1:44 pm
RE: log4javascript – thanks, actually took your suggestion from your Enterprise JavaScript Error Handling slides, to build a bridge to PHP where I use Zend different log writers: file, email, etc…
Now I am trying to figure out how to integrate the ‘productionize’ (thanks for sharing that btw) above in my existing Ajax code, understanding the benefits and/or of keeping the function stack intact when debugging.
For now I have enable the ‘debugging’ awareness state of javascript by having PHP inform it if the codebase is running on local dev, server dev, qa or prod.
As a side note I was surprised how good the IE debugger is, was able to catch innocuous but deadly syntax err which Firebug did not pickup. As of now the step-through of IE is much better then FB.
Paul A. Boivin on January 27th, 2010 at 9:07 am
There is a small mistake:
The pattern I now recommend is to completely remove the try-catch statement when in debug mode.
…
function doSomething(value){
if (!debugMode){
process(value);
} else {
try {
process(value);
} catch (ex){
log(1, “doSomething(): ” + ex.message);
}
}
}
(there is shouldn’t be Not operator in condition)
Andrey on April 28th, 2010 at 2:57 am
nicholas, i think it should be if (debugMode) in the second codeblock, not if (!debugMode)
function doSomething(value){
if (!debugMode){ // <<---- here!
process(value);
} else {
try {
process(value);
} catch (ex){
log(1, "doSomething(): " + ex.message);
}
}
}
schnalle on April 28th, 2010 at 6:57 am
Thanks Andrey and Schnalle, I’ve updated the code. Funny it was around for a year before anyone noticed.
Nicholas C. Zakas on April 28th, 2010 at 10:39 pm
Hi!
You are always creating the same function in a loop. Wouldn’t that be faster?
var createSaveFunction = function(name, method){
return function(){
try {
return method.apply(this, arguments);
} catch (ex) {
log(1, name + “(): ” + ex.message);
}
};
};
function productionize(object){
var name,
method;
for (name in object){
method = object[name];
if (typeof method == “function”){
object[name] = createSaveFunction(name, method);
}
}
}
Other then that, I LOVE your idea, definitly will try it
Best reagrds
Roland
Roland on August 31st, 2010 at 7:44 pm
PS: Also shouldn’t the for-in have an if(object.hasOwnProperty(name)) to not overwrite prototype functions?
Roland on August 31st, 2010 at 7:46 pm
What if the constructor takes references of its functions, for example to create listeners?
What if the class’s methods are called by direct reference to the prototype?
The scheme basically won’t work.
nerd on September 1st, 2010 at 10:17 am
Comments are automatically closed after 14 days.