The material, the translation of which we are publishing today, is dedicated to handling JS errors using
window.onerror
. This is a special browser event that is triggered when uncaught errors occur. Here we will talk about how to intercept errors using an
onerror
event
onerror
, and how to send information about them to the website developer server. This handler can be used as the basis of your own system for collecting and analyzing information about errors. In addition, it is one of the most important mechanisms used in error-oriented libraries, such as
raven-js .

Listening for the window.onerror event
You can listen for the
onerror
event by assigning a
window.onerror
function that plays the role of an error handler:
window.onerror = function(msg, url, lineNo, columnNo, error) { // ... ... return false; }
This function is called when an error occurs, and the following arguments are passed to it:
msg
- error message. For example - Uncaught ReferenceError: foo is not defined
.url
- the address of the script or document in which the error occurred. For example - /dist/app.js
.lineNo
is the line number in which the error occurred (if supported).columnNo
is the column number of the row (if supported).error
- the object of the error (if supported).
The first four arguments tell the developer which script, which row, and which column of the row the error occurred. The last argument, an object of type
Error
, is perhaps the most important of all the arguments. Let's talk about it.
Error object and Error.prototype.stack property
At first glance, there is nothing special about the
Error
object. It contains three completely standard properties -
message
,
fileName
and
lineNumber
. This data, given the information passed to the
window.onerror
event handler, can be considered redundant.
The real value in this case is a non-standard property
Error.prototype.stack
. This property gives access to the call stack (error stack), allows you to know what was happening in the program at the time of the error, what functions call preceded its appearance. Tracing the call stack can be an important part of the debugging process. And, despite the fact that the
stack
property is not standard, it is available in all modern browsers.
Here is the
stack
property of the error object in Chrome 46.
"Error: foobar\n at new bar (<anonymous>:241:11)\n at foo (<anonymous>:245:5)\n at <anonymous>:250:5\n at <anonymous>:251:3\n at <anonymous>:267:4\n at callFunction (<anonymous>:229:33)\n at <anonymous>:239:23\n at <anonymous>:240:3\n at Object.InjectedScript.\_evaluateOn (<anonymous>:875:140)\n at Object.InjectedScript.\_evaluateAndWrap (<anonymous>:808:34)"
Before us is an unformatted string. When the contents of this property are presented in this form, it is inconvenient to work with it. Here's how the same will look after formatting.
Error: foobar at new bar (<anonymous>:241:11) at foo (<anonymous>:245:5) at callFunction (<anonymous>:229:33) at Object.InjectedScript._evaluateOn (<anonymous>:875:140) at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)
Now, after formatting, the error stack looks much clearer, it immediately becomes clear why the
stack
property is very important when debugging errors.
However, everything here is not smooth. The
stack
property is not standardized, it is implemented differently in different browsers. Here, for example, looks like an error stack in Internet Explorer 11.
Error: foobar at bar (Unknown script code:2:5) at foo (Unknown script code:6:5) at Anonymous function (Unknown script code:11:5) at Anonymous function (Unknown script code:10:2) at Anonymous function (Unknown script code:1:73)
It can be noted, in comparison with the previous example, that not only a different presentation format of stack frames is used here, but also that less data is given for each frame. For example, Chrome identifies instances of using the
new
keyword and provides more detailed information about other events (in particular, function calls.
_evaluateOn
and.
_evaluateAndWrap
). In this case, we compared only what is issued by IE and Chrome. Other browsers have used their own approaches to displaying data about the stack and to the selection of information included in this data.
In order to bring all this to a uniform form, you can use third-party tools. For example, in raven-js TraceKit is used for this. Stacktrace.js and some other projects serve the same purpose.
Features support window.onerror different browsers
The
windows.onerror
event has existed in browsers for quite some time. In particular, it can be found in IE6 and in Firefox 2. The problem here is that all browsers implement
windows.onerror
differently. For example, this concerns the number and structure of the arguments passed to the event handlers of this event.
Here is a table that contains information about the arguments passed to the
onerror
handler in the main browsers.
Browser
| message
| url
| lineNo
| colNo
| errorObj
|
Firefox
| there is
| there is
| there is
| there is
| there is
|
Chrome
| there is
| there is
| there is
| there is
| there is
|
Edge
| there is
| there is
| there is
| there is
| there is
|
IE 11
| there is
| there is
| there is
| there is
| there is
|
IE10
| there is
| there is
| there is
| there is
| Not
|
IE 9.8
| there is
| there is
| there is
| Not
| Not
|
Safari 10 and up
| there is
| there is
| there is
| there is
| there is
|
Safari 9
| there is
| there is
| there is
| there is
| Not
|
Android Browser 4.4
| there is
| there is
| there is
| there is
| Not
|
It is probably not surprising that Internet Explorer 8, 9, and 10 have limited
onerror
support. However, it may seem unusual that in Safari browser support for the error object appeared only in the 10th version, released in 2016. In addition, there are outdated mobile devices that use the standard Android browser, which also does not support the error object. In modern versions of Android, this browser is replaced by Chrome Mobile.
If we have no error object at our disposal, then there is no stack trace data either. This means that browsers that do not support the error object do not provide, in the standard
onerror
handler
onerror
, stack information. And this, as we have said, is very important.
Designing a polyfill for window.onerror using a try / catch construct
In order to get information about the stack in browsers that do not support passing an
onerror
object to the
onerror
handler, you can use the following trick. You can wrap the code in a
try/catch
construct and catch the errors yourself. The resulting error object will contain, in all modern browsers, what we need - the
stack
property.
Take a look at the code of the
invoke()
helper method, which calls the specified object method, passing in an array of arguments.
function invoke(obj, method, args) { return obj[method].apply(this,args); }
Here's how to use it.
invoke(Math, 'max', [1,2])
Here is the same
invoke()
, but now the method call is wrapped in
try/catch
, which allows you to intercept possible errors.
function invoke(obj, method, args) { try { return obj[method].apply(this,args); } catch(e) { captureError(e);// throw e;// } } invoke(Math,'highest',[1,2]); // , Math.highest
Of course, it is very expensive to manually add such structures to all places where they may be needed. This task can be simplified by creating a universal auxiliary function.
function wrapErrors(fn) { // if(!fn.__wrapped__) { fn.__wrapped__ = function() { try{ return fn.apply(this,arguments); }catch(e){ captureError(e);// throw e;// } }; } return fn.__wrapped__; } var invoke = wrapErrors(function(obj, method, args) { returnobj[method].apply(this,args); }); invoke(Math,'highest',[1,2]);//, Math.highest
Since JavaScript uses a single-threaded code execution model, this wrapper should be used only with those function calls that are at the beginning of new stacks. There is no need to wrap absolutely all function calls into it.
As a result, it turns out that this function should be used in the following places:
- Where the application starts (for example, when using jQuery, in the
$(document).ready
function) - In event handlers (for example, in
addEventListener
or in constructions of the form $.fn.click
) - In callbacks triggered by timer events (for example, this is
setTimeout
or requestAnimationFrame
)
Here is an example of using the
wrapErrors
function.
$(wrapErrors(function () {// doSynchronousStuff1();// setTimeout(wrapErrors(function () { doSynchronousStuff2();// })); $('.foo').click(wrapErrors(function () { doSynchronousStuff3();// })); }));
Such constructions can be added to the code yourself, but this is too time consuming task. As a convenient alternative in such situations, you can consider libraries to work with errors, which, for example, have mechanisms that
addEventListener
and
setTimeout
tools to intercept errors.
Transfer of errors to the server
So, now we have at our disposal means for intercepting error information either using
windows.onerror
or using auxiliary functions based on
try/catch
. These errors occur on the client side, and, after intercepting them, we would like to deal with them and take measures to eliminate them. For this they need to be transferred to our server. In order to do this, you need to prepare a web service that would accept information about errors via HTTP, after which it would somehow save them for further processing, say, write to a log file or database.
If this web service is located on the same domain as the web application,
XMLHttpRequest
will be sufficient. The following example shows how to use a function to execute AJAX requests from jQuery to transfer data to a server.
function captureError(ex){ var errorData = { name:ex.name,// : ReferenceError message:ex.line,// : x is undefined url:document.location.href, stack:ex.stack// ; , ! }; $.post('/logger/js/',{ data:errorData }); }
Keep in mind that if you need to send cross-domain requests to send information about errors to the server, you will have to take care of the support of such requests.
Results
You have learned the basics of creating a service to intercept errors and send information about them to the server. In particular, here we considered the following questions:
- Features of the
onerror
event and its support in various browsers. - Using the
try/catch
mechanism to get information about the call stack in cases where onerror
does not support working with the error object. - Transmission of data about errors to the server of the developer.
After learning how the mechanisms described above work, you acquired basic knowledge that will allow you to start creating your own system for working with errors, specifying additional details during the work. Perhaps this scenario is especially relevant for those cases when it comes to some application in which, say, for security reasons, there are no plans to use third-party libraries. If your application allows the use of third-party code, you may well find the right tool for monitoring JS errors. Among such tools are
Sentry ,
Rollbar ,
TrackJS and other similar projects.
Dear readers! What tools for monitoring JS errors do you use?
