Diagnosing JavaScript Errors Faster with Error.stack


IE10 in Windows 8 Consumer Preview includes support for Error.stack, which enables Web developers to diagnose and correct bugs faster, especially those that are difficult to reproduce. Developers can build amazing apps with the capabilities of Web platforms that power today’s modern browsers. In Windows 8, we expose that power through both Internet Explorer 10 and Metro style apps in JavaScript. The increasing power and complexity of these apps means developers need better tools like Error.stack for handling errors and diagnosing bugs.

Debugging Applications

Structured error handling in JavaScript rests on throw and try/catch – where the developer declares an error and passes the control flow to a portion of the program that deals with error handling. When an error is thrown, Chakra, the JavaScript engine in Internet Explorer, captures the chain of calls that led up to where the error originated – also referred to as the call stack. If the object being thrown is an Error (or is a function whose prototype chain leads back to Error), Chakra creates a stack trace, a human-readable listing of the call stack. This listing is represented as a property, stack, on the Error object. The stack includes the error message, function names, and source file location information of the functions. This information can help developers rapidly diagnose defects by learning what function was being called, and even see what line of code was at fault. For example, it might indicate that a parameter passed into the function was null or an invalid type.

Let’s explore with an example of a simple script that attempts to calculate the distance between two points, (0, 2) and (12, 10):

(function () {

'use strict';

function squareRoot(n) {

if (n < 0)

throw new Error('Cannot take square root of negative number.');

 

return Math.sqrt(n);

}

 

function square(n) {

return n * n;

}

 

function pointDistance(pt1, pt2) {

return squareRoot((pt1.x - pt2.x) + (pt1.y - pt2.y));

}

 

function sample() {

var pt1 = { x: 0, y: 2 };

var pt2 = { x: 12, y: 10 };

 

console.log('Distance is: ' + pointDistance(pt1, pt2));

}

 

try {

sample();

}

catch (e) {

console.log(e.stack);

}

})();

This script has a bug – it forgets to square the differences of the components. As a result, for some inputs, the pointDistance function will simply return incorrect results; other times, it will cause an error. To understand the stack trace, let’s examine the error in the F12 developer tools and look at its Script tab:

Screen shot of the F12 developer tools showing a stack trace logged by calling console.log(e.stack) where e is the Error object passed to the catch clause of a try/catch block.

The stack trace is dumped to the console in the catch clause, and because it’s at the top of the stack, it’s readily apparent that the Error is originating in the squareRoot function. To debug the problem, a developer wouldn’t have to go very deep into the stack trace; squareRoot’s precondition was violated, and looking one level up the stack, it’s clear why: the sub-expressions within the call to squareRoot should themselves be parameters to square.

While debugging, the stack property can help identify code for setting a breakpoint. Bear in mind that there are other ways to view the call stack: for instance, if you set the script debugger to “Break on caught exception” mode, you may be able to inspect the call stack with the debugger. For deployed applications, you may consider wrapping problematic code within try/catch in order to capture failed calls, and log them to your server. Developers would then be able to look at the call stack to help isolate problem areas.

DOM Exceptions and Error.stack

I noted previously that the object being thrown must be an Error or lead back to Error via its prototype chain. This is intentional; JavaScript supports throwing any object and even primitives as exceptions. While all of these can be caught and examined, they are not all specifically designed to contain error or diagnostic information. As a consequence, only Errors will be updated with a stack property when thrown.

Even though DOM Exceptions are objects, they don’t have prototype chains that lead back to Error, and therefore they don’t have a stack property. In scenarios in which you are performing DOM manipulation and want to surface JavaScript-compatible errors, you may want to wrap your DOM manipulation code within a try/catch block, and throw a new Error object within the catch clause:

function causesDomError() {

try {

var div = document.createElement('div');

div.appendChild(div);

} catch (e) {

throw new Error(e.toString());

}

}

However, you will probably want to consider whether you want to use this pattern. It is likely best-suited for utility library development; specifically, consider whether your code’s intent is to hide DOM manipulation, or to simply carry out a task. If it is hiding DOM manipulation, wrapping the manipulation and throwing an Error is probably the right way to go.

Performance Considerations

The construction of the stack trace begins when the error object is thrown; doing so requires walking the current execution stack. To prevent performance problems in traversing a particularly large stack (possibly even a recursive stack chain), by default, IE only collects the top 10 stack frames. This setting is configurable, however, by setting the static property Error.stackTraceLimit to another value. This setting is global, and must be changed prior to throwing the error, or else it will not have an effect on stack traces.

Asynchronous Exceptions

When a stack trace is generated from an asynchronous callback (for example, a timeout, interval, or XMLHttpRequest), the asynchronous callback, rather than the code that created the asynchronous callback, will be at the bottom of the call stack. This has some potential implications for tracking down offending code: if you use the same callback function for multiple asynchronous callbacks, you may find it difficult to determine by inspection alone which callback caused the error. Let’s slightly modify our previous sample so that, instead of calling sample() directly, we’ll put that into a timeout callback:

(function () {

'use strict';

function squareRoot(n) {

if (n < 0)

throw new Error('Cannot take square root of negative number.');

 

return Math.sqrt(n);

}

 

function square(n) {

return n * n;

}

 

function pointDistance(pt1, pt2) {

return squareRoot((pt1.x - pt2.x) + (pt1.y - pt2.y));

}

 

function sample() {

var pt1 = { x: 0, y: 2 };

var pt2 = { x: 12, y: 10 };

 

console.log('Distance is: ' + pointDistance(pt1, pt2));

}

 

setTimeout(function () {

try {

sample();

}

catch (e) {

console.log(e.stack);

}

}, 2500);

})();

Upon executing this snippet, you’ll see the stack trace come up after a slight delay. This time, you’ll also see that the bottom of the stack is not Global Code but rather is Anonymous function. In fact, it’s not the same anonymous function, but the callback function passed into setTimeout. Since you lose the context surrounding hooking up the callback, you may not be able to determine what is causing the callback to be called. If you consider a scenario in which one callback is registered to handle the click event of many different buttons, you will be unable to tell to which callback the registration refers to. That being said, this limitation is only minor, because, in most cases, the top of the stack will likely highlight the problem areas.

Exploring the Test Drive Demo

Screen shot of Test Drive demo Explore Error.stack

Check out this Test Drive demo using IE10 in the Windows 8 Consumer Preview. You can execute code in the context of an eval and if an error occurs, you’ll be able to inspect it. If you’re running the code within IE10, you’ll also be able to highlight the lines of your code as you hover over the error lines in the stack trace. You can type code into the Code area yourself, or select from several samples in the list. You can also set the Error.stackTraceLimit value when running the code samples.

For reference material, you may want to cruise over to the MSDN documentation for Error.stack as well as stackTraceLimit.

—Rob Paveza, Program Manager, Chakra Runtime

Comments (16)

  1. Arieta says:

    Man, I wish this was possible with Windows 7!

  2. FremyCompany says:

    It could be used to enforce calling policy on certain functions (make sure your function was called by another function of the same file, within some class boundaries, etc…), but performance would probably be quite bad, making this a no-go. It would be a fun experiment nonetheless.

    BTW, do the Chacka team plan to support Source code maps in IE10?

  3. Brian says:

    > "Cannot take square root of negative number."

    Why not? Usually it is taught in elementary math that the square root of -1 is i. This can easily be expanded to any negative number.

  4. Prior Semblance says:

    Because javascript integers don't have a value for i.

  5. Ted says:

    Pushing for more standardized script language like CoffeScript would greatly help to replace javascript with a more developer friendly language.

  6. Brian says:

    > Prior Semblance

    Then use two floats, one for the real part and the other for the imaginary part. Then format them into a string with i for display.

    The code is already wrapping Math.sqrt, so there is ample opportunity to improve it.

  7. alvatrus says:

    @Brian:

    And next time you'll be asking for Laplace transformations? (Which still is basic math.)

  8. Dont feed the troll says:

    named Brian.

  9. @Brian says:

    Do you demand unnecessary complexity from all code examples?

  10. jens says:

    Java Script has no future — never

  11. Erik Arvidsson says:

    Actually WebIDL mandates that exceptions have Error.prototype on the prototype chain.

  12. Todd says:

    Use strict?!?! Wtf?!?! Is this another disaster in the making like option explicit?!?? Or "on error resume next"?!?!

    Please for the love of god DO NOT INVENT your own crap and put it into JavaScipt you've already ruined web development with IE don't make it worse please.

    Oh and what ever happened to the comments on the post about the IE metruck keyboards that told you loud and clear that smiley faces don't belong on a numerical, email or URL keyboard!!!!

    MSFT totally blew us off again when we had plenty of helpful advice on how insanely bad your first fail keyboards were.

    Do us a favor and and least report in this blog that you are addressing your designers spectacular goof!!!!

    We are sooooo pi$$$$ed with the lack of communication from Microsoft.  If you want to get into the mobile game 10 years late you seriously need to talk openly with developers because you obviously don't have a clue what you're doing!

    And what is with this retarded blog software?!?!?! How ______ing hard is it to make a comment form that submits content vs swallowing it.  ____ing disgraceful blog software. Get off of it ASAP please!!!!!

  13. thanks.. says:

    Good job..

  14. PhistucK says:

    @Todd -

    "use strict"; is part of ECMAScript 5+. Not a Microsoft invention (at least not only theirs, I guess) and not a Microsoft only convention.

    It is not mandatory in any way and it can only help you code better, while being backward compatible (it is just a string for browsers/engine that do not know how to interpret it).

  15. Colin says:

    @Kyle – where did @Todd indicate that he used Google Chrome? Do you have a troll hate on for Chrome? Are you bothered that it is a far superior browser to IE?

    @Todd's comments about the strict setting might have been incorrect but we all share his fear that Microsoft will once again try and strong arm cruft into the Web that has not been endorsed by the W3C, ECMAScript or similar bodies.

    As for the keyboards mentioned he is refering to this blog post. blogs.msdn.com/…/guidelines-for-building-touch-friendly-sites.aspx which clearly shows horrible keyboard layouts

    that do not make sense for their designed purpose:

    All keyboards have an emoticon button – none other than "general text" need it! Certainly not a phone number, url or email!

    Numeric keyboards (number/telephone) should have a keyboard to flip to the alpha keyboard too.  This is critical for overcoming things like U.S. Developer based sites that think the entire world only uses numerical postal codes. and phone numbers with extensions. e.g. (555) 555-1234 x 345

    Why is the backspace key bigger than the enter key?  Do you already have less confidence in Windows users being able to type on your blocky keyboard?

    Why does the url keyboard (not shown on the blog, but shown in various screen shots around the web) not contain the colon ":" key? This is fairly critical for many urls as is the ampersand "&" key.

    Kudos for adding arrow keys (left/right) though as these are painfully missing on most onscreen keyboards!

    Since Microsoft claims that they are now listening to feedback from Users and Developers a little follow up about how these keyboard layouts are going to be fixed would be very helpful in explaining that Microsoft development isn't the black box we believe it to be.

    This blog would also be the best place to provide such info now that MS Connect is still useless and abandoned.

    PS I do find the topic of error tracing in IE to be quite humorous though considering there was no mention or apology for how atrocious it has been to do in IE over the last 10 years due to inadequate and unhelpful MS tools – even though we've been complaining about it since IE6.  The slap in the face was so pleasant. A simple: "Hey, we realize that debugging in IE was next to impossible in the past – and we sincerely apologize for that…." prefix statement would have gone a long, long way to earning back some respect and trust from developers you've scorned over the last decade.