Throwing Better Errors Using Stack

A while ago, I wrote about using the throw statement for debugging. Unfortunately, the title of that post confused the issue somewhat; it was never my intention to suggest that the throw statement should replace alert() for debugging your own scripting. Nor was I advocating using it for messages to end users.

The point was, when you’re writing a third-party library or API—that is, a script that’s intended for developers to program with, rather than for end users—the throw statement can be useful as part of a validation mechanism. It can give developers’ feedback directly in the JavaScript console, instead of constantly throwing alerts.

Since writing that post, I’ve used the technique several times. But the problem that keeps cropping up is how, when you manually throw an error, the filename and line number reported with the error is often unhelpful.

The values you receive with an error object point to the file and line where the error was thrown; however, if you’re throwing an error manually to validate developer input, what you really want to tell them is the filename and line-number of their code, where their error actually is.

So, can we get that information?

Indeed we can, at least in Firefox. Both Firefox and WebKit browsers (Safari, Chrome, and so on) expose a stack property of the error object, which is a stack trace of the error. It lists each individual statement that led up to the error: from where it actually occurred, back through any calling statements, to the highest abstraction or event.

We can parse that stack (a single string value) to get the details we want; whatever is at the bottom of the stack is usually where the developer’s input was. This function will do the trick:

function fail(message){   var inputerror = new Error();   inputerror.name = "nMyScript/ValidationError: ";   inputerror.message = message;   if(typeof inputerror.stack != "undefined")   {      var errorstack = inputerror.stack.split(/s*(@|at)s*/);      errorstack = errorstack[errorstack.length - 1]                   .replace(/^s+|s+$/g, '')                   .split(/:([0-9]+)/);      inputerror.fileName = errorstack[0];      inputerror.lineNumber = errorstack[1];   }   return inputerror;}

This would then be used like this:

throw(fail("Illegal value for foo"));

I say that what we’re looking for is usually at the bottom of the stack, because that represents the highest level of abstraction. If the developer’s input itself is not the highest level (for example, if it’s abstracted into a function literal), then the bottom of the trace will point to that instead.

At least, it does in Firefox!

Opera already shows a stack trace in its error output, so the extra information we want is there anyway. But the WebKit browsers don’t show filename or line number in their output, even though they do expose the necessary error properties, and the code above works.

But everybody tests in Firefox, don’t they? Regardless of what’s used for browsing!

Thumbnail credit: kagey_b

note:Want more?

If you want to read more from James, subscribe to our weekly tech geek newsletter, Tech Times.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

No Reader comments

Comments on this post are closed.