392

I would like to catch every undefined function error thrown. Is there a global error handling facility in JavaScript? The use case is catching function calls from flash that are not defined.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • What do you want to do with an error once you catch it? Do you just need to log it so you can create the missing function, or are you looking to stop exceptions from breaking your code? – Dan Herbert Jun 04 '09 at 17:00
  • 2
    I would like to get the name of the missing function called and based on presence of some string call my own function. Any call to a function with the string 'close' would call my close() for example. I would also like to trap the error at that point. –  Jun 04 '09 at 17:06
  • 2
    https://exceptionsjs.com provides this functionality and can be taylored to only catch errors related to undefined functionality with its "guard" functionality. – Steven Wexler Aug 04 '14 at 02:48

9 Answers9

673

How to Catch Unhandled Javascript Errors

Assign the window.onerror event to an event handler like:

<script type="text/javascript">
window.onerror = function(msg, url, line, col, error) {
   // Note that col & error are new to the HTML 5 spec and may not be 
   // supported in every browser.  It worked for me in Chrome.
   var extra = !col ? '' : '\ncolumn: ' + col;
   extra += !error ? '' : '\nerror: ' + error;

   // You can view the information in an alert to see things working like this:
   alert("Error: " + msg + "\nurl: " + url + "\nline: " + line + extra);

   // TODO: Report this error via ajax so you can keep track
   //       of what pages have JS issues

   var suppressErrorAlert = true;
   // If you return true, then error alerts (like in older versions of 
   // Internet Explorer) will be suppressed.
   return suppressErrorAlert;
};
</script>

As commented in the code, if the return value of window.onerror is true then the browser should suppress showing an alert dialog.

When does the window.onerror Event Fire?

In a nutshell, the event is raised when either 1.) there is an uncaught exception or 2.) a compile time error occurs.

uncaught exceptions

  • throw "some messages"
  • call_something_undefined();
  • cross_origin_iframe.contentWindow.document;, a security exception

compile error

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • setTimeout("{", 10);, it will attempt to compile the first argument as a script

Browsers supporting window.onerror

  • Chrome 13+
  • Firefox 6.0+
  • Internet Explorer 5.5+
  • Opera 11.60+
  • Safari 5.1+

Screenshot:

Example of the onerror code above in action after adding this to a test page:

<script type="text/javascript">
call_something_undefined();
</script>

Javascript alert showing error information detailed by the window.onerror event

Example for AJAX error reporting

var error_data = {
    url: document.location.href,
};

if(error != null) {
    error_data['name'] = error.name; // e.g. ReferenceError
    error_data['message'] = error.line;
    error_data['stack'] = error.stack;
} else {
    error_data['msg'] = msg;
    error_data['filename'] = filename;
    error_data['line'] = line;
    error_data['col'] = col;
}

var xhr = new XMLHttpRequest();

xhr.open('POST', '/ajax/log_javascript_error');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
    if (xhr.status === 200) {
        console.log('JS error logged');
    } else if (xhr.status !== 200) {
        console.error('Failed to log JS error.');
        console.error(xhr);
        console.error(xhr.status);
        console.error(xhr.responseText);
    }
};
xhr.send(JSON.stringify(error_data));

JSFiddle:

https://jsfiddle.net/nzfvm44d/

References:

Alexander Taubenkorb
  • 3,031
  • 2
  • 28
  • 30
Sam
  • 26,946
  • 12
  • 75
  • 101
  • 3
    worth mentionning firefox doesn't give back the error message when `throw` is made manually. http://stackoverflow.com/questions/15036165/catching-error-event-message-from-firefox – Sebas Mar 30 '13 at 02:09
  • 2
    Great answer. You can implement the "Report this error via ajax" with a package like JSNLog, which does the ajax and server side logging for you. – user1147862 Nov 16 '13 at 10:02
  • 4
    In addition to this answer, I added the err object so I could get the stack trace. http://stackoverflow.com/a/20972210/511438. Now I can develop with more feedback because my dev errors appear as a box at the top of the page (as I have created it). – Valamas Jun 23 '14 at 03:02
  • Wouldn't it be better to pack your onerror implementation in a try-catch(ignore) block, preventing your onerror() to throw even more errors? (not that it's the case here, but just to be sure) – Pieter De Bie Aug 14 '15 at 08:57
  • @Sam, How can you catch only runtime errors not compile time errors? – Pacerier Feb 15 '17 at 22:32
  • @Pacerier You can use `error instanceof SyntaxError`. See [MDN instanceof docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) and [MDN SyntaxError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError). Not sure what the older browser support is for this, but MDN suggests it's supported across the board. – Sam Feb 17 '17 at 20:53
  • As you say, this handles *unhandled* errors. If a library is catching an error and making an alert or something, the error won't make it to window.onerror. – Noumenon Oct 11 '17 at 18:32
  • it does not work now as Nov 2017, error in latest Chrome and IE 11 does not get captured by global error handler – super1ha1 Nov 16 '17 at 03:53
  • 1
    @super1ha1 Can you provide a jsfiddle? I don't think browsers would introduce a breaking change where the onerror event handler stops firing events. You can try this jsfiddle to see the onerror handler working: https://jsfiddle.net/nzfvm44d/ This still works for me in Chrome version 62.0.3202.94 (Official Build) (64-bit). – Sam Nov 17 '17 at 17:36
  • What is the syntax if I want a named function? window.onerror=MyErrorFunction – Cymro Dec 24 '19 at 13:28
  • @Sam about the browser support section of your great answer, what was your source, even linked MDN page is not same as yours. – Mehdi Dehghani Jan 20 '20 at 12:19
  • @MehdiDehghani it looks like I got it from https://dev.opera.com/articles/better-error-handling-with-window-onerror/ . Generally speaking I go with the docs in MDN, but I'm certain IE had support for this before IE9. It's shocking to me that the docs for this event aren't better. Hopefully they're better now than they were when I answered this 8 yrs ago. – Sam May 13 '20 at 23:19
  • This does not work for errors fired inside a dynamically populated iframe, any workaround for that? – raquelhortab Dec 21 '21 at 10:33
  • @raquelhortab You need to capture the error within the iframe using a global error handler like this post discusses. Once you capture the error in the iframe, it's up to you what you want to do with it. You can ajax the error to capture it on a server or communicate the error to the main window (example https://stackoverflow.com/questions/9153445/how-to-communicate-between-iframe-and-the-parent-site). – Sam Dec 21 '21 at 21:42
205

Does this help you:

<script type="text/javascript">
window.onerror = function() {
    alert("Error caught");
};

xxx();
</script>

I'm not sure how it handles Flash errors though...

Update: it doesn't work in Opera, but I'm hacking Dragonfly right now to see what it gets. Suggestion about hacking Dragonfly came from this question:

Mimic Window. onerror in Opera using javascript

Community
  • 1
  • 1
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
41

sophisticated error handling

If your error handling is very sophisticated and therefore might throw an error itself, it is useful to add a flag indicating if you are already in "errorHandling-Mode". Like so:

var appIsHandlingError = false;

window.onerror = function() {
    if (!appIsHandlingError) {
        appIsHandlingError = true;
        handleError();
    }
};

function handleError() {
    // graceful error handling
    // if successful: appIsHandlingError = false;
}

Otherwise you could find yourself in an infinite loop.

SunnyRed
  • 3,525
  • 4
  • 36
  • 57
  • 30
    Or a more fail-safe way would be to use try-catch around the `handleError` method. – Aidiakapi Apr 26 '12 at 14:49
  • 8
    You can't use try catch if you have ansynchronous call in you'r error handling. So he's solution remain e good solution – Emrys Myrooin Jul 03 '15 at 13:04
  • @EmrysMyrooin Still it will cause a sync or an async loop by error handling and probably crash without usable stack info. – inf3rno Aug 25 '16 at 22:11
  • 1
    I think the flag should be reset at some point, right? I believe we need a try/catch around if big if, and make sure the flag is reset right before leaving the onerror method. Otherwise, only one error will be handled. – Seb Mar 10 '17 at 17:39
26

It seems that window.onerror doesn't provide access to all possible errors. Specifically it ignores:

  1. <img> loading errors (response >= 400).
  2. <script> loading errors (response >= 400).
  3. global errors if you have many other libraries in your app also manipulating window.onerror in an unknown way (jquery, angular, etc.).
  4. probably many cases I haven't run into after exploring this now (iframes, stack overflow, etc.).

Here is the start of a script that catches many of these errors, so that you may add more robust debugging to your app during development.

(function(){

/**
 * Capture error data for debugging in web console.
 */

var captures = [];

/**
 * Wait until `window.onload`, so any external scripts
 * you might load have a chance to set their own error handlers,
 * which we don't want to override.
 */

window.addEventListener('load', onload);

/**
 * Custom global function to standardize 
 * window.onerror so it works like you'd think.
 *
 * @see http://www.quirksmode.org/dom/events/error.html
 */

window.onanyerror = window.onanyerror || onanyerrorx;

/**
 * Hook up all error handlers after window loads.
 */

function onload() {
  handleGlobal();
  handleXMLHttp();
  handleImage();
  handleScript();
  handleEvents();
}

/**
 * Handle global window events.
 */

function handleGlobal() {
  var onerrorx = window.onerror;
  window.addEventListener('error', onerror);

  function onerror(msg, url, line, col, error) {
    window.onanyerror.apply(this, arguments);
    if (onerrorx) return onerrorx.apply(null, arguments);
  }
}

/**
 * Handle ajax request errors.
 */

function handleXMLHttp() {
  var sendx = XMLHttpRequest.prototype.send;
  window.XMLHttpRequest.prototype.send = function(){
    handleAsync(this);
    return sendx.apply(this, arguments);
  };
}

/**
 * Handle image errors.
 */

function handleImage() {
  var ImageOriginal = window.Image;
  window.Image = ImageOverride;

  /**
   * New `Image` constructor. Might cause some problems,
   * but not sure yet. This is at least a start, and works on chrome.
   */

  function ImageOverride() {
    var img = new ImageOriginal;
    onnext(function(){ handleAsync(img); });
    return img;
  }
}

/**
 * Handle script errors.
 */

function handleScript() {
  var HTMLScriptElementOriginal = window.HTMLScriptElement;
  window.HTMLScriptElement = HTMLScriptElementOverride;

  /**
   * New `HTMLScriptElement` constructor.
   *
   * Allows us to globally override onload.
   * Not ideal to override stuff, but it helps with debugging.
   */

  function HTMLScriptElementOverride() {
    var script = new HTMLScriptElement;
    onnext(function(){ handleAsync(script); });
    return script;
  }
}

/**
 * Handle errors in events.
 *
 * @see http://stackoverflow.com/questions/951791/javascript-global-error-handling/31750604#31750604
 */

function handleEvents() {
  var addEventListenerx = window.EventTarget.prototype.addEventListener;
  window.EventTarget.prototype.addEventListener = addEventListener;
  var removeEventListenerx = window.EventTarget.prototype.removeEventListener;
  window.EventTarget.prototype.removeEventListener = removeEventListener;

  function addEventListener(event, handler, bubble) {
    var handlerx = wrap(handler);
    return addEventListenerx.call(this, event, handlerx, bubble);
  }

  function removeEventListener(event, handler, bubble) {
    handler = handler._witherror || handler;
    removeEventListenerx.call(this, event, handler, bubble);
  }

  function wrap(fn) {
    fn._witherror = witherror;

    function witherror() {
      try {
        fn.apply(this, arguments);
      } catch(e) {
        window.onanyerror.apply(this, e);
        throw e;
      }
    }
    return fn;
  }
}

/**
 * Handle image/ajax request errors generically.
 */

function handleAsync(obj) {
  var onerrorx = obj.onerror;
  obj.onerror = onerror;
  var onabortx = obj.onabort;
  obj.onabort = onabort;
  var onloadx = obj.onload;
  obj.onload = onload;

  /**
   * Handle `onerror`.
   */

  function onerror(error) {
    window.onanyerror.call(this, error);
    if (onerrorx) return onerrorx.apply(this, arguments);
  };

  /**
   * Handle `onabort`.
   */

  function onabort(error) {
    window.onanyerror.call(this, error);
    if (onabortx) return onabortx.apply(this, arguments);
  };

  /**
   * Handle `onload`.
   *
   * For images, you can get a 403 response error,
   * but this isn't triggered as a global on error.
   * This sort of standardizes it.
   *
   * "there is no way to get the HTTP status from a 
   * request made by an img tag in JavaScript."
   * @see http://stackoverflow.com/questions/8108636/how-to-get-http-status-code-of-img-tags/8108646#8108646
   */

  function onload(request) {
    if (request.status && request.status >= 400) {
      window.onanyerror.call(this, request);
    }
    if (onloadx) return onloadx.apply(this, arguments);
  }
}

/**
 * Generic error handler.
 *
 * This shows the basic implementation, 
 * which you could override in your app.
 */

function onanyerrorx(entity) {
  var display = entity;

  // ajax request
  if (entity instanceof XMLHttpRequest) {
    // 400: http://example.com/image.png
    display = entity.status + ' ' + entity.responseURL;
  } else if (entity instanceof Event) {
    // global window events, or image events
    var target = entity.currentTarget;
    display = target;
  } else {
    // not sure if there are others
  }

  capture(entity);
  console.log('[onanyerror]', display, entity);
}

/**
 * Capture stuff for debugging purposes.
 *
 * Keep them in memory so you can reference them
 * in the chrome debugger as `onanyerror0` up to `onanyerror99`.
 */

function capture(entity) {
  captures.push(entity);
  if (captures.length > 100) captures.unshift();

  // keep the last ones around
  var i = captures.length;
  while (--i) {
    var x = captures[i];
    window['onanyerror' + i] = x;
  }
}

/**
 * Wait til next code execution cycle as fast as possible.
 */

function onnext(fn) {
  setTimeout(fn, 0);
}

})();

It could be used like this:

window.onanyerror = function(entity){
  console.log('some error', entity);
};

The full script has a default implementation that tries to print out a semi-readable "display" version of the entity/error that it receives. Can be used for inspiration for an app-specific error handler. The default implementation also keeps a reference to the last 100 error entities, so you can inspect them in the web console after they occur like:

window.onanyerror0
window.onanyerror1
...
window.onanyerror99

Note: This works by overriding methods on several browser/native constructors. This can have unintended side-effects. However, it has been useful to use during development, to figure out where errors are occurring, to send logs to services like NewRelic or Sentry during development so we can measure errors during development, and on staging so we can debug what is going on at a deeper level. It can then be turned off in production.

Hope this helps.

spky
  • 2,123
  • 2
  • 17
  • 21
Lance
  • 75,200
  • 93
  • 289
  • 503
  • 1
    Apparently images trigger the error event: http://stackoverflow.com/a/18152753/607033 – inf3rno Aug 25 '16 at 22:05
  • This doesn't seem to be working for me for – Kevin Wheeler Oct 03 '22 at 15:54
25

Let me explain how to get stacktraces that are reasonably complete in all browsers.

Error handling in JavaScript

Modern Chrome and Opera fully support the HTML 5 draft spec for ErrorEvent and window.onerror. In both of these browsers you can either use window.onerror, or bind to the 'error' event properly:

// Only Chrome & Opera pass the error object.
window.onerror = function (message, file, line, col, error) {
    console.log(message, "from", error.stack);
    // You can send data to your server
    // sendError(data);
};
// Only Chrome & Opera have an error attribute on the event.
window.addEventListener("error", function (e) {
    console.log(e.error.message, "from", e.error.stack);
    // You can send data to your server
    // sendError(data);
})

Unfortunately Firefox, Safari and IE are still around and we have to support them too. As the stacktrace is not available in window.onerror we have to do a little bit more work.

It turns out that the only thing we can do to get stacktraces from errors is to wrap all of our code in a try{ }catch(e){ } block and then look at e.stack. We can make the process somewhat easier with a function called wrap that takes a function and returns a new function with good error handling.

function wrap(func) {
    // Ensure we only wrap the function once.
    if (!func._wrapped) {
        func._wrapped = function () {
            try{
                func.apply(this, arguments);
            } catch(e) {
                console.log(e.message, "from", e.stack);
                // You can send data to your server
                // sendError(data);
                throw e;
            }
        }
    }
    return func._wrapped;
};

This works. Any function that you wrap manually will have good error handling, but it turns out that we can actually do it for you automatically in most cases.

By changing the global definition of addEventListener so that it automatically wraps the callback we can automatically insert try{ }catch(e){ } around most code. This lets existing code continue to work, but adds high-quality exception tracking.

var addEventListener = window.EventTarget.prototype.addEventListener;
window.EventTarget.prototype.addEventListener = function (event, callback, bubble) {
    addEventListener.call(this, event, wrap(callback), bubble);
}

We also need to make sure that removeEventListener keeps working. At the moment it won't because the argument to addEventListener is changed. Again we only need to fix this on the prototype object:

var removeEventListener = window.EventTarget.prototype.removeEventListener;
window.EventTarget.prototype.removeEventListener = function (event, callback, bubble) {
    removeEventListener.call(this, event, callback._wrapped || callback, bubble);
}

Transmit error data to your backend

You can send error data using image tag as follows

function sendError(data) {
    var img = newImage(),
        src = 'http://yourserver.com/jserror&data=' + encodeURIComponent(JSON.stringify(data));

    img.crossOrigin = 'anonymous';
    img.onload = function success() {
        console.log('success', data);
    };
    img.onerror = img.onabort = function failure() {
        console.error('failure', data);
    };
    img.src = src;
}
Slava Knyazev
  • 5,377
  • 1
  • 22
  • 43
Fizer Khan
  • 88,237
  • 28
  • 143
  • 153
  • What is _yourserver.com/jserror_ ? *REST, Web Service, Wcf Service* ? Any simple about backend? – Kiquenet Sep 24 '15 at 11:32
  • If you want to send js errors from user browser to your server. you have to write your backend(`http://yourserver.com`) to receive and store. If you choose https://www.atatus.com/, you dont need to do anything. Just include two lines of script in your page. – Fizer Khan Sep 25 '15 at 06:53
6
// display error messages for a page, but never more than 3 errors
window.onerror = function(msg, url, line) {
if (onerror.num++ < onerror.max) {
alert("ERROR: " + msg + "\n" + url + ":" + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;
GibboK
  • 71,848
  • 143
  • 435
  • 658
6

If you want unified way to handle both uncaught errors and unhandled promise rejections you may have a look on uncaught library.

EDIT

<script type="text/javascript" src=".../uncaught/lib/index.js"></script>

<script type="text/javascript">
    uncaught.start();
    uncaught.addListener(function (error) {
        console.log('Uncaught error or rejection: ', error.message);
    });
</script>

It listens window.unhandledrejection in addition to window.onerror.

Slava Knyazev
  • 5,377
  • 1
  • 22
  • 43
  • 2
    Lance Pollard in [the answer above](https://stackoverflow.com/a/36317375/52277) mentioned a few errors, that normal onerror doesn’t handle. Does you library handle them? If some of them, please specify which? – Michael Freidgeim May 04 '19 at 23:07
  • @MiFreidgeimSO-stopbeingevil Just checked the source code - no unfortunately it doesn't. Aleksandr Oleynikov: Would be awesome if you can support this - I'll then turn my downvote to an upvote ;-) – brillout Apr 01 '20 at 06:51
5

One should preserve the previously associated onerror callback as well

<script type="text/javascript">

(function() {
    var errorCallback = window.onerror;
    window.onerror = function () {
        // handle error condition
        errorCallback && errorCallback.apply(this, arguments);
    };
})();

</script>
Apoorv Saxena
  • 4,086
  • 10
  • 30
  • 46
2

I would recommend giving Trackjs a try.

It's error logging as a service.

It's amazingly simple to set up. Just add one <script> line to each page and that's it. This also means it will be amazingly simple to remove if you decide you don't like it.

There are other services like Sentry (which is open-source if you can host your own server), but it doesn't do what Trackjs does. Trackjs records the user's interaction between their browser and your webserver so that you can actually trace the user steps that led to the error, as opposed to just a file and line number reference (and maybe stack trace).

AbdelHady
  • 9,334
  • 8
  • 56
  • 83
kane
  • 5,465
  • 6
  • 44
  • 72
  • 2
    TrackJS [doesn't seem to have a free tier](https://trackjs.com/pricing/) anymore, though there is a free trial. – pkaeding May 24 '15 at 21:52
  • This is fine for tracking and being alerted to errors but doesn't really solve the handling part. Ideally I think the asker is looking for a way to handle these errors so that the rest of the code still runs. – wgp Aug 30 '15 at 17:43
  • What if you catch the error event and add an xhr call to the logger with the stack trace and the application state? How is trackjs better? – inf3rno Aug 25 '16 at 22:09
  • 2
    @inf3rno it's more than a stack trace, which just tracks the beginning of the error. TrackJS will track the entire user session so you can see what led up to it. here's a screenshot as an example https://trackjs.com/assets/images/screenshot.png – kane Aug 26 '16 at 15:47