Skip to content

Instantly share code, notes, and snippets.

@JamesMGreene
Last active October 4, 2015 12:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JamesMGreene/3ded0f6e7f0a658b9394 to your computer and use it in GitHub Desktop.
Save JamesMGreene/3ded0f6e7f0a658b9394 to your computer and use it in GitHub Desktop.
Proposal: Add window.getLastError (or modify invocation arguments of window.onerror)

Proposal: Add window.getLastError() (or modify window.onerror(...), etc.)

Error handling in an isolated scope

In our applications, most of us hopefully follow the UX best practice of catching unexpected and/or unpreventable errors and logging them back to the server-side for monitoring.

In isolated scopes, this can be achieved by setting up a try-catch block, which also allows you to inspect the pertinent Error object itself:

try {
    throw new Error("WTF");
} catch (e) {
    alert("Isolated error! Details: " + e);
}

This is very useful, especially in browsers that support the stack property of the Error object to improve "debugability". This property's availability is also of great benefit to @eriwen's StackTrace.js, which I find to be handy (though I'm not a big fan of the StrackTrace.js API, as @eriwen and I have discussed previously).

Error handling at the global scope

Setting up layers of try-catch blocks quickly becomes unreasonable in large applications. As an alternative, we can attach a listener to the window object's error event, which is invoked whenever an error bubbles up without being handled elsewhere:

var oldOnError = window.onerror;
window.onerror = function myErrorHandler(errorMsg, url, lineNumber, columnNumber) {
    if (oldOnError) {
        return oldOnError(errorMsg, url, lineNumber, columnNumber);
    }

    alert("Globally unhandled error! Details: " + errorMsg);
    return false;
}

The problem with window.onerror

The problem is that this mechanism does not allow us to inspect the pertinent Error object at all: it only provides us with four arguments at invocation time: message (string), fileName (string), lineNumber (number), and columnNumber (number). These are OK but not very immediately useful in practice.

My proposal(s) to fix it

As such, I propose the following options as fixes:

  1. Add a function like getLastError() to the global window object that would fetch the actual Error object associated with the most recent unhandled error. I would foresee common usage looking something like the following:
var oldOnError = window.onerror;
window.onerror = function myErrorHandler(errorMsg, url, lineNumber, columnNumber) {
        if (oldOnError) {
            return oldOnError(errorMsg, url, lineNumber, columnNumber);
        }

        var e = window.getLastError();
        alert("Globally unhandled error! But we now can discover its origin. Details: " + e);
        return false;
}
  1. Alternatively (though less preferably), we could also update the invocation arguments of window.onerror callbacks to include a new fifth argument that would be the relevant Error object itself:
    var oldOnError = window.onerror;
    window.onerror = function myErrorHandler(errorMsg, url, lineNumber, columnNumber, e) {
        if (oldOnError) {
            return oldOnError(errorMsg, url, lineNumber, columnNumber, e);
        }
    
        alert("Globally unhandled error! But we now can discover its origin. Details: " + e);
        return false;
    }
    While this essentially makes the first four arguments useless, I have posed it this way for the sake of backward compatibility.
  2. We could also add a property to the window object, such as window.error which would basically be a getter method to achieve the same results as the aforementioned window.getLastError() proposal.
  3. To better align with Node.js, we could also handle this via a new uncaughtException event, which would basically be a parallel to the window.onError callback except that it would receive the actual Error object (and only the Error object) as an argument. However, it is important to note that the uncaughtException event is typically emitted immediately before exiting the Node.js application (i.e. it is a terminal error), which is a behavior we would not want to replicate for a browser environment.

Errata

When [synchronously] handling an Error in an isolated scope, the active Error object should be equivalent to the result of the window.getLastError() function invocation:

try {
    throw new Error("WTF");
} catch (e) {
    assert(e === window.getLastError());  // true
}

Summary

I know I can't be alone here, especially since there are logged bugs for this in many trackers. For example: Mozilla's bug #355430 and StackTrace.js's Issues #26 and #9.

So... thoughts?

@Raynos
Copy link

Raynos commented May 8, 2012

You forgot this === e inside the onerror callback. Which I believe is what IE does currently.

I think the notion that the target of the event is the error makes sense.

@JamesMGreene
Copy link
Author

@Raynos:
Not so, this is always equal to window in the global onerror callback. If you can show me otherwise, please do.

Here's my test case: http://jsfiddle.net/6mXqv/56/embedded/result/

@Raynos
Copy link

Raynos commented May 10, 2012

The statement was confusing, this isn't currently implemented

What I meant to say that this === e is a good solution to propose.

I believe IE has already implemented this a propitiatory extension

@JamesMGreene
Copy link
Author

I've never seen such behavior in IE either. So this is an existing proprietary extension? I would like to see a reference if you have one.

In actuality, though, I would have to disagree with you: I don't think that this === e is a good solution. The ideal state for event callbacks — though, as WHATWG tells me, this isn't actually an "event" (browser implementors seem to disagree) — is having this bound to whatever the actual callback itself was bound to. This proposal features one of the rare cases in which we actually do want this to be bound to window.

@ndbroadbent
Copy link

@JamesMGreene, thanks very much for raising this issue. It will be incredibly helpful to debug JavaScript errors with a proper error object that provides much more information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment