Make your 'Script error.' issues more insightful in Sentry

Context and problem

For quite some time we had our Sentry monitoring capturing the following very unperceptive error:

Event ErrorEvent captured as exception with message Script error.

Eventually I landed on this rather comprehensive Sentry answers article: What is "Script Error"?.

According to the article...

“Script error” is what browsers send to the onerror callback when an error originates from a JavaScript file served from a different origin (different domain, port, or protocol).

The article gives the recommendation of using crossorigin="anonymous" on the script that originated the error.

The problem is that the exception captured in Sentry has absolutely no reference to the culprit script, leaving us clueless on how to address the issue.

Vision of an improvement

The obvious improvement would be to be provided with the name of the culprit script.

At best, to be pointed at the line, in that script, that originated the unhandled exception.

A solution

Experimenting with the window.error callback presented in the article, I was able to log the url argument that revealed the chased script.

Now, it was a matter of figuring out how to tell Sentry to let us send them a better formatted error message, that would include the clue about which script is the culprit, as well as some insight how to get even more details.

This is how I made it all work...

First, in the configuration passed to the Sentry.init() function call, make sure to include within the ignoreErrors array the string: 'Script error.'. That basically tells Sentry to ignore this error all together.

Secondly, where appropriate in your use case, include a window.onerror callback definition like the one below:

/**
 * Customises specific error before capturing to Sentry. It aims at providing
 * more insights than what the default Sentry capture would do for such errors.
 *
 * The parameters are the one provided by the `window.onerror` callback.
 *
 * @param {string} message Error message.
 * @param {string} url URL of the script which threw the exception.
 * @param {object} [error] Error being thrown.
 */
function addSentryOverrides(message, url, error) {
  // Warning: For this to work, 'Script error.' should be added to Sentry.init
	// `ignoreErrors` list.
	if (error === undefined && message && message.includes('Script error.')) {
		const formattedMessage = `Unhandled exception in script ${url}. Hint: Add \`crossorigin="anonymous"\` on the script tag to know the type of error and the position of the error`;

		const scriptError = new Error(formattedMessage);
		scriptError.name = 'ScriptError';

		Sentry.captureException(scriptError);
	}
}

window.onerror = function (message, url, _line, _column, error) {
	addSentryOverrides(message, url, error);
};

Testing out the solution

In order to test out this solution, I created a temporary script over at Static Save that looks like this:

function foo() {
  bar();
}

When calling foo() this should blow up as bar() is not defined.

Now let's use this...

After loading the page I use for the test; in the developer console, I went ahead and appended the temporary script programmatically as below:

var culpritRemoteScript = document.createElement('script');

culpritRemoteScript.setAttribute('src','https://temp.staticsave.com/68525ff025b09.js');
document.body.appendChild(culpritRemoteScript);

I then executed the below snippet in order to trigger the exception upon a user click interaction.

// Clicking the Home link will generate an Unhandled exception
document
  .querySelector('[data-test="home-link"]')
  .addEventListener('click', function clickEventHandler(event) {
  event.preventDefault();
	foo();  
});

Now, as soon as we click the "Home" anchor link, the foo() function will be called, what in turns will be caught into the window.error callback defined previously, what would finally capture the following exception in Sentry:

ScriptError
Unhandled exception in script https://temp.staticsave.com/68525ff025b09.js. Hint: Add crossorigin="anonymous" on the script tag to know the type of error and the position of the error

This is much more insightful, isn't it?

Sentry benefit from following the provided hint

Taken that we follow the "Hint" of adding the crossorigin attribute to the remote script, Sentry seems to capture the exception beautifully, in the sense that, it turns into the following error message and stack trace

Message:

ReferenceError
bar is not defined

Strack trace:

ReferenceError: baz is not defined
  at bar(/68525ff025b09.js:2:3)

As you can see it now reveals quite some insights: the error type, the script filename, and the position of the statement that threw the exception.

We are therefore better equipped to go and fix that bug.

Sharing is caring

I spent quite some time in researching how to address this inconvenience, as well as, crafting this solution.

I therefore hope that, if you find yourself in this situation, your path will cross this blog post and that it will fit to your need and make your day brighter 😉.

If you find any problem with the proposed solution and/or have a better approach, do not hesitate to reach out. So far this is the best one I think of.