JavaScript error handling best practices

| 4 min. (807 words)

When creating large web applications in JavaScript it is critical to implement a robust error handling process, whether coding on the server or in the browser. The latter environment is a wild west of differing specs and legacy versions under which your code must run, and unless you have unlimited resources for QA, you won’t be able to test and find all the edge cases. As ECMAScript is a null and exception-based language, error handlers should be present at the top levels of your code paths, in order to catch bugs which aren’t caught or handled in their scope. Browsers, being as delightful as they are, add a few gotchas to a task that should be bread and butter. In this post I’ll highlight a few of these so you can ensure you’re picking up every error possible that occurs while your users are running your client-side web app or site.

window.onerror: the global handler

It is true that with modern browsers, hooking window.onerror for errors that bubble all the way to the top along with adding jQuery event handlers for Ajax errors will catch practically all Error objects thrown in your client code. If you’re manually setting up a handler for window.onerror, in modern browsers this is done with `window.addEventListener(error;, callback)`, while in IE8 you need to call `window.attachEvent(;onerror;, callback)`.

Note that you should then consider the environment in which these errors are being handled, and the reason for doing so. It is one thing to catch as many errors as possible with their stacktraces, but the advent of modern F12 dev tools solves this use case when implementing and debugging locally. Breakpoints etc will give you more data than is available from the handlers, especially for errors thrown by third-party libraries which were loaded from CORS requests. You need to take additional steps to instruct the browser to provide this data.

The key issue is providing this data in production, as your users are guaranteed to run a far wider array of browsers and versions than you can possibly test, and your site/app will break in unexpected ways, no matter how much QA you throw at it.

To handle this, you need a production error tracker which picks up every error thrown in your user’s browsers, as they use your code, and sends them to an endpoint where the data can be viewed by you and used to fix the bugs as they happen. At Raygun we’ve put a bunch of effort into providing a great experience for this, as there’s many pitfalls and issues to consider that a naive implementation will miss.

Dealing with minification and source mapping

For instance, chances are you’ll be bundling and minifying your JS assets, which means that errors thrown from minified code will have junk stacktraces with mangled variable names. For this, you need your build tool to generate source maps (we recommend UglifyJS2 for this part of the pipeline), and your error tracker to accept and process these, turning the mangled stacktraces back into human-readable ones. Raygun does all this out of the box, and includes an API endpoint to accept source maps as they are generated by your build process. This is key as they need to be kept non-public, otherwise anyone could unminify your code, negating its purpose.

The raygun4js client library also comes with `window.onerror` for both modern and legacy browsers, as well as jQuery hooks out-of-the-box. To set this up you only need to follow two easy steps:

Step 1

Add this snippet to your markup, before the closing </head> tag:

<script type="text/javascript">
!function(a,b,c,d,e,f,g,h){a.RaygunObject=e,a[e]=a[e]||function(){
  (a[e].o=a[e].o||[]).push(arguments)},f=b.createElement(c),g=b.getElementsByTagName(c)[0],
  f.async=1,f.src=d,g.parentNode.insertBefore(f,g),h=a.onerror,a.onerror=function(b,c,d,f,g){
  h&&h(b,c,d,f,g),g||(g=new Error(b)),a[e].q=a[e].q||[],a[e].q.push({
  e:g})}}(window,document,"script","//cdn.raygun.io/raygun4js/raygun.min.js","rg4js");
</script>

Step 2

Add the following lines to your JS site code just before the closing body tag and paste in your API key (from your Raygun dashboard):

<script type="text/javascript">
rg4js('apiKey', 'paste_your_api_key_here');
  rg4js('enableCrashReporting', true);
</script>

View detailed set up instructions here

Third party scripts (CORS)

There’s also a bunch of functionality built-in including the ability to mutate the error payload before it is sent, adding tags and custom data, metadata on the user who saw the error. It also takes the pain out of getting good stack traces from the above-mentioned third-party CORS scripts, which solves the dreaded ;Script Error; (which contains no error message, and no stack trace).

A more crucial issue is that due to the huge audience on the web, your site will generate many thousands of duplicate instances of each error. An error tracking service like Raygun has smarts to roll these up into error groups so you don’t drown in a flood of notifications, and lets you see each actual error ready to be fixed.

Ready to implement these JavaScript error handling best practices? Sign up for your free 14 day trial here. Or if you’ve got questions about JavaScript error handling let us know in the comments below.