The art of throwing JavaScript errors

When I was younger, the most befuddling part of programming languages was the ability to create errors. My first reaction to the throw operator in Java was, “well that’s stupid, why would you ever want to cause an error?” Errors were the enemy to me, something I sought to avoid, so the ability to cause an error seemed like a useless and dangerous aspect of the language. I thought it was dumb to include the same operator in JavaScript, a language that people just didn’t understand in the first place. Now with a great deal of experience under my belt, I’m a big fan of throwing my own errors. Doing so can lead to easier debugging and code maintenance when done properly.

When programming, an error occurs when something unexpected happens. Maybe the incorrect value was passed into a function or a mathematical operation had an invalid operand. Programming languages define a base set of rules that, when deviated from, result in errors so that the developer can fix the code. Debugging would be nearly impossible if errors weren’t thrown and reported back to you. If everything failed silently, it would take you a long time to notice that there was an issue in the first place, let alone isolate and fix it. Errors are the friends of developers, not enemies.

The problem with errors is that they tend to pop up in unexpected places and at unexpected times. To make matters worse, the default error messages are usually too terse to really explain what went wrong. JavaScript error messages are notoriously uninformative and cryptic (especially in Internet Explorer), which only compounds the problem. Imagine if an error popped up with a message that said, “this function failed because this happened.” Instantly, your debugging task becomes easier. This is the advantage of throwing your own errors.

It helps to think of errors as built-in failure cases. It’s always easier to plan for a failure at a particular point in code than it is to anticipate failure everywhere. This is a very common practice in product design, not just in code. Cars are built with crumple zones, areas of the frame that are designed to collapse in a predictable way when impacted. Knowing how the frame will react in a crash, which parts will fail, allow the manufacturers to ensure passenger safety. Your code can be constructed in the same way.

Even though JavaScript has come a long way in the past few years, JavaScript developers still have far less tools to aid in debugging than developers of other languages. Throwing errors in your JavaScript is arguably more valuable than in any other language due to the difficulties around debugging. You can throw an by using the throw operator and providing an object to throw. Any type of object can be thrown, however, an Error object is the most typical to use:

throw new Error("Something bad happened.")

When you throw an error in this way, and the error isn’t caught via a try-catch statement, the browser will display the error text in the browser’s typical way. For Internet Explorer, this means a little icon in the lower-left corner of the browser is displayed and a dialog with the error text is displayed when that icon is double clicked; Firefox with Firebug installed will show the error in the console; Safari and Chrome output the error onto the Web Inspector; Opera shows the error in the Error Console. In other words, it’s treated the same way as an error that you didn’t throw.

The difference is that you get to provide the exact text to be displayed by the browser. Instead of just line and column numbers, you can include any information that you’ll need to successfully debug the issue. I typically recommend that you always include the function name in the error message as well as the reason why the function failed. Consider the following function:

function addClass(element, className){
    element.className += " " + className;
}

This function’s purpose is to add a new CSS class to the given element (a very common method in JavaScript libraries). But what happens if element is null? You’ll get a cryptic error message such as, “object expected.” Then, you’ll need to look at the execution stack (if your browser supports it) to actually locate the source of the problem. Debugging becomes much easier by throwing an error:

function addClass(element, className){
    if (element != null && typeof element.className == "string"){
        element.className += " " + className;
    } else {
        throw new Error("addClass(): First arg must be a DOM element.");
    }
}

Discussions about accurately detecting whether an object is a DOM element or not aside, this method now provides better messaging when it fails due to an invalid element argument. Seeing such a verbose message in your error console immediately leads you to the source of the problem. I like to think of throwing errors as leaving post-it notes for myself as to why something failed.

Understanding how to throw errors is just one part of the equation; understanding when to throw errors is the other. Since JavaScript doesn’t have type- or argument-checking, a lot of developers incorrectly assume that they should implement that for every function. Doing so is impractical and can adversely affect the overall script’s performance. The key is to identify parts of the code that are likely to fail in a particular way and only throw errors there. In short, only throw errors where errors will already occur.

If a function is only ever going to be called by known entities, error checking is probably not necessary (this is the case with private functions); if you cannot identify all the places where a function will be called ahead of time, then you’ll likely need some error checking and will even more likely benefit from throwing your own errors. The best place for throwing errors is in utility functions, those functions that are a general part of the scripting environment and may be used in any number of places. This is precisely the case with JavaScript libraries.

All JavaScript libraries should throw errors from their public interfaces for known error conditions. YUI/jQuery/Dojo/etc. can’t possibly anticipate when and where you’ll be calling their functions. It’s their job to tell you when you’re doing stupid things. Why? Because you shouldn’t have to debug into their code to figure out what went wrong. The call stack for an error should terminate in the library’s interface, no deeper. There’s nothing worse than seeing an error that’s 12 functions deep into a library; library developers have a responsibility to prevent this from happening.

This also goes for private JavaScript libraries. Many web applications have their own proprietary JavaScript libraries either built with or in lieu of the well-known public options. The goal of libraries is to make developers’ lives easier, and this is done by providing an abstraction away from the dirty implementation details. Throwing errors helps to keep those dirty implementation details hidden safely away from developers.

JavaScript also provides a try-catch statement that is capable of intercepting thrown errors before they are handled by the browser. Typically, developers have trouble discerning whether it’s appropriate to throw an error or catch one using try-catch. Errors should only be thrown in the deepest part of the application stack which, as discussed previously, typically means JavaScript libraries. Any code that handles application-specific logic should have error handling capabilities and should therefore be catching errors thrown from the lower-level components.

Application logic always knows why it was calling a particular function and so is best suited for handling the error. It’s important to mention that you should never have a try-catch statement with an empty catch clause; you should always be handling errors in some way. This may be different in development versus production, but it must be done. If an error happens, the answer should never be to simply wrap it in a try-catch and let it be – this masks an error rather than dealing with it.

Throwing errors in JavaScript is an art. It takes time to feel out where the appropriate parts of your code should throw errors. Once you figure this out, however, you’ll find that your debugging time will decrease and your satisfaction with the code will increase.

Comments

  1. David Stamm

    An inspiring post - thanks, Nicholas! It is lame indeed to step through library code in Firebug because you passed incorrect params to a library API.

    I'd be interested to hear more about your distinction between when to use try/catch and when to use throw. As I understand it, you are recommending that we use throw for low-level mishaps (like Java's FileNotFoundException). And then we catch those thrown exceptions with code at a higher level of abstraction, like business logic.

  2. henrah

    It is certainly better for library methods to throw clear, descriptive errors rather than complicated or deep errors that obscure the nature of the problem. However, it's not sensible to throw a custom, descriptive error whenever a library method gets invalid input, as in the case of the addClass method above. Surely the most important aspect of an API is consistency, and in order to be consistent you would need to validate the input of every public API method. In practical terms, this is equivalent to implementing a type system in javascript from scratch.

    Javascript libraries could certainly benefit by throwing more custom errors, and by attempting to fail as early as possible. But error-throwing can only be a complement to proper documentation and unit-testing of the library. You cannot entirely prevent invalid input to methods, and when there is invalid input there will ultimately be undefined behavior. Sometimes undefined behavior of the libarry means emitting an ugly error from internal methods and sometimes it means failing silently. Regardless, it is much more important to document the semantics of a library and to test them rigorously, than to stop developers from "doing stupid things."

    You can't use errors to teach users how an API works. That level of defensive programming simply isn't possible in Javascript. You can use errors to signal to developers when valid input has yielded error conditions, but you can't use them to enforce the semantics of the library itself. If developers are habitually sending the wrong type of argument to a library method, then you either have a design problem or a documentation problem. Either way, the problem cannot be solved by adding safeguards in the code.

  3. Robert Cerny

    I have learned to appreciate the "throw early" guideline in Java, in particular in a public API. But when it comes to JavaScript i would like to put into consideration, that the code is public, so a developer can - in the above example - figure out very quickly why the method "addClass" might have failed. Additionally i would emphasize that you clog your code with if statements where the execution in a correct program (hopefully when it goes into production) is only going one way and the exception is never thrown. For this reason i consider it good practice to outsource the contract checking into something which is only active during development e.g. similar to Java's assert or design by contract. Nevertheless, i agree that this subject needs more attention in the JavaScript community.

  4. Michael Schøler

    Let's JSLint validate the code - why not :-) And what about the className parameter - we need to take care verifying everything when embracing this line of though as library developers. It may even lead to problems when claiming that the first param is a DOM element by detecting a .className attribute on the object, further checks could be added, although there is no way to be entirely sure.

    Secondly, I think any exeption should be thrown as early as possible in the code, and not as the final statement within a function. I believe this could prevent maintenance coders from adding code before the sanity checks.

    So, to be thorough, the error handling/exception throwing code should be:

    function addClass(element, className) {
    if (typeof className !== "string") {
    throw new Error("addClass(): First arg must be a class name.");
    }
    if (typeof element === "undefined" || element === null || typeof element.className !== "string" || typeof element.tagName !== "string") {
    throw new Error("addClass(): Second arg must be a DOM element.");
    }
    element.className += " " + className;
    }

    Do you agree on this?

  5. Nicholas C. Zakas

    @henrah - I wasn't suggesting that libraries throw errors every time and under every circumstance. I'm saying you need to figure out the most obvious ways that the library will fail and throw errors for those. This prevents people from debugging your library. I agree that errors aren't a tool to learn a library, but they are a way to help users figure out when they're making obvious mistakes. Documentation is great, but we all know that documentation rarely gets read as thoroughly as necessary.

    @Robert - Just because the source code is available doesn't always mean that the developer can figure out why the library is failing. Weaving through the inner workings of libraries such as YUI and jQuery is daunting, especially for beginning and intermediate developers.

    @Michael - You're going a bit overboard. My point was to throw errors in places where it has the most value, not in all places and under all circumstances. In this example, className being a number, null, an object, or anything other than a string won't cause a JavaScript error so there's no point in throwing an error there. It may cause your UI to show up incorrectly, but that's not a JavaScript error.

  6. Nicholas C. Zakas

    Apologies to other commenters (Jakob and Mats). For some reason, Wordpress deleted some comments and I've not been able to get them back.

    For those who mentioned throwing errors at the top, yes, that is preferable when possible. It's not always possible, of course.

    There is also a difference between code verification (via JSLint) versus throwing errors to indicate problems. Verification happens at build time and thus prevent build-time errors; throwing errors helps runtime debug runtime issues. Two different approaches for two different problems.

  7. Robert Cerny

    Yes, i agree that many JavaScript libraries create wrinkles on one's forehead, including my own :-) But my point was, that developer convenience is not the only thing to put into consideration, there is other ways to learn an API than to shoot random code at it and see what happens, even though it is a lot of fun :-) Generally i try to avoid productive code that is never passed when an API is used correctly. One thing that i did is to build a runtime type system for JavaScript[1], which is based on method call interception. This helps me get the program correct and can be deactivated in production. More general: the contract checking (e.g. parameters are ok and the call can succeed) are preferably done in the development and test stage, but not in production. Just think about how many times the if condition in "addClass" is evaluated and how many times it goes only one way.

    When you are developing a Java lib which is distributed without the source, it is a different story. Then early exceptions are a very good practice.

    [1] http://www.cerny-online.com...

  8. Michael Schøler

    Perhaps I'm going overboard with regards to className. My point being that when ever a developer drags in a new library, all faults, self-inflicted or not, will initially be blamed on the library.

    When thinking of throwing javascript errors "as an art", we need to debate if verification and correct input is up to the library or the developers.

    While testing "intra library" calls in the library development phase it might be usefull to have checks on everything in all circumstances, and then it would agreed be a good strategy to trim out certain non-crucial assertments when releasing the library.

  9. Mike Wilson

    As for using exceptions for multi-level error handling in larger applications I have successfully mimicked Java's nested exceptions model in JavaScript, resulting in a stack of exceptions (one from each error handling layer in the application). The top message is usually suitable to present to a user while the deepest message is interesting for the developer.

  10. Ben Gerrissen

    Error throwing on wrong arguments (early on) is good actually, since thats your first line of defence against errors deeper in the code. Error throwing deeper on in the code, would only serve a purpose while developing a library and you want to get rid of deep error throwing once you release the library, unless it involves handling arguments further, like string parsing.

    In any case, it's good someone highlights error throwing, since thats a rarely used feature in javascript whilst it can help out so much.

  11. Laszlo Janszky

    Hi, nice article! Btw I think it's useful to defining different error types, but I didn't find any article about this.
    I am using now this function for error handling, cause I'm lazy to write the function name by every errors;

    function assert(condition,message)
    {
    if (!condition)
    {
    var callers="";
    var current=arguments.callee;
    var depth=0;
    while((current=current.caller) && assert.depth!=depth)
    {
    callers+="\n\n{"+depth+"}\n\n"+current;
    depth++;
    }
    throw(message+callers);
    }
    }
    assert.depth=1;

    and I think it's not enough for me. In example I don't know in a try-catch block which type of error I have to catch and which not, cause I have just one error type.

  12. Laszlo Janszky

    The main problem with javascript event handling is, that the onerror event handler is bad designed:
    window.onerror(msg,file,line)
    It's not using Error objects, just the message. In IE Error object doesn't have fileName or lineNumber properties, so you have to use onerror to get this info, but in FF this info is just in Error object available by throwed exceptions. So you can decide in which browser your error handling have to work.
    I choosed FF, but there is another problem; you cannot create custom Error classes, cause file and line infos are missing.
    So overall you can choose beetween file infos and custom exception objects....

  13. Nicholas C. Zakas

    @Robert - Let's make a distinction between helping developers learn your library versus helping developers debug their code that uses your library. I do agree that extra validating is really useful during development and may not all necessarily be needed in production, but I do believe that well-placed errors are necessary even in production to help debug issues. We all know that errors don't occur just during development, and knowing exactly what went wrong on someone else's computer is really useful.

    @Laszlo - There are different types of errors, maybe I'll cover that next week. Be very careful when using caller, as this is not part of the ECMAScript standard (and not implemented by Opera). The error event is a hack, and isn't the way you should be handling errors in web applications (and not all browsers support it). You can actually create custom error classes so long as they inherit from Error (via prototype chaining) and you'll get all the standard information on that object.

  14. Laszlo Janszky

    Yepp, I thinked a lot about this, and I decided to use the browsers Error object with a fixed error message. I think file and line is important, but function hierarchy can help a lot too. Maybe this is the best solution in javascript.

    Exception=Error;
    SyntaxException=window.SyntaxError || Error;
    TypeException=window.TypeError || Error;
    ReferenceException=window.ReferenceError || Error;
    RangeException=window.RangeError || Error;
    EvalException=window.EvalError || Error;

    Exception.createMessage=function (text)
    {
    text=typeof(text)=="string"?text:"";
    var callers=[];
    var current=arguments.callee.caller;
    while(current=current.caller)
    {
    callers.push(current);
    }
    var message="\nmessage: "+text;
    if (callers.length)
    {
    var lineString="\n------------------------------------------\n";
    var callerString=callers.map(function (caller,depth)
    {
    return "{"+depth+"}\n"+caller.toString();
    }).join("\n\n");
    message+=lineString+callerString+lineString;
    }
    return message;
    }
    $E=Exception.createMessage;

    A short example:

    if (typeof(index)!="number")
    {
    throw new TypeException($E("Index is not a number."));
    }

    Ohh and a short map function, if somebody doesn't know it:

    Array.prototype.map=function (method,context)
    {
    for (var i=0,l=this.length,results=[]; i<l; i++)
    {
    results.push(method.call(context || this,this[i],i));
    }
    return results;
    }

    @Nicholas - Yepp, opera doesn't have caller, but in that way callee.caller is undefined, so my script won't write the function list. It's not a big deal...

  15. Laszlo Janszky

    @Nicholas - "but in that way callee.caller is undefined" Oops! :-) or not.. Thanks, I fixed it.

    .
    .
    .
    Exception.createMessage=function (text)
    {
    text=typeof(text)=="string"?text:"";
    if (!arguments.callee.caller)
    {
    return text;
    }
    .
    .
    .

  16. Maan Mehta

    Hi,

    Throwing Exceptions in Java has performance impact and is not considered a best practice. What about the same in JavaScript wrt to Performance. It may be convenient to use this mechanism for Error Handling, but is it the best way wrt Performance?

    Regards
    Maan

  17. Asen Bozhilov

    Hello Nicholas Zakas.
    From which reasons, you make a type checking between the parts from your own code?

    if (element != null && typeof element.className == "string")

    That looks useless. Documentation should be require proper objects for value of `element` argument. Moreover you test implementation dependent string value returned from `typeof`. `typeof`fo for a host objects return implementation dependent primitive string value.
    After that we have throw error.

    throw new Error("addClass(): First arg must be a DOM element.");

    That error will confused me. Especially if i pass object for which:

    typeof element.className != 'string';

    Documentation for `addClass` doesn't say in explicit way:

    element should be a DOM element.

    So i am free to pass for `element` something other objects:

    addClass({}, 'className'); //Runtime error
    addClass({className : 'string'}, 'className'); //???

    In conclusion, i think that example is much more confused from explained proper way of throwing runtime error.

    Btw: Can you explain, why you test the type of `className` value? Especially when after that, you use internal:

    [[Get]] and [[Put]] method of that object. I don't see any reasonable purposes to test the type of `className` property value.

  18. Adams

    @Lazzlo:

    You could create a new Error object to get stack and linenumber.
    Have only tested this in FF.


    CustInvalidArgumentException = function(arg){
    var eo = new Error();
    var stackv = eo.stack.split(/\n/);
    if(stackv.length>1) stackv.splice(1,1);
    this.stack = stackv.join("\n");
    this.arg = arg;
    var tmp = stackv.length > 1 ? stackv[1].match(/@(.+?):([0-9]+)$/) : [""];
    this.fileName = tmp.length>1?tmp[1]:"N/A";
    this.lineNumber = tmp.length>1?tmp[2]:"N/A";
    this.message = " is not a valid argument.";
    this.name = "CustInvalidArgumentException";

    this.toString = function() {
    return "\n"+this.name+
    "\n Msg : \""+this.arg.toString() + "\" " + this.message+
    "\n File Name : "+this.fileName+
    "\n Line : "+this.lineNumber+
    "\n Stack:\n"+this.stack;
    };
    };
    function test(argv) {
    // valid arguments + defaults
    this.argv={
    a:3,
    b:5,
    debug:true
    };
    for(x in this.valid)
    if(argv.hasOwnProperty(x))
    this.argv[x] = argv[x];
    try {
    for(x in argv)
    if(!this.argv.hasOwnProperty(x))
    throw new CustInvalidArgumentException(x);
    }catch (err) {
    if(this.argv.debug) throw err;
    }
    }

    var TEST_ERROR = test({
    a: 3,
    b: 4,
    c: 77
    });

    Could result in something like:


    Error: uncaught exception:
    CustInvalidArgumentException
    Msg : "c" is not a valid argument.
    File Name : http://dev.mordor.dev/tst/errjs.php
    Line : 163
    Stack:
    Error()@:0
    test([object Object])@http://dev.mordor.dev/tst/errjs.php:63
    @http://dev.mordor.dev/tst/errjs.php:72

Understanding JavaScript Promises E-book Cover

Demystify JavaScript promises with the e-book that explains not just concepts, but also real-world uses of promises.

Download the Free E-book!

The community edition of Understanding JavaScript Promises is a free download that arrives in minutes.