Script error: What causes a Script error and how to solve them

| 8 min. (1594 words)

“Script error” is one of the most cryptic error messages you may encounter. The main grievance is it provides no information about the actual cause of the problem. However, this secrecy is not a bug, but a feature that intends to protect your site from malicious attacks.

Please refer to the Raygun technical documentation regarding Script errors for quick fixes to the most common causes.

What are Script errors?

Script errors occur when a client-side script that violates the same-origin policy of the user’s browser by making an invalid cross-origin HTTP request.

If you’re working on a website and have Raygun Crash Reporting hooked into your client-side JavaScript, “Script error” will probably be one of the first things you will notice appearing in your dashboard.

What causes Script errors?

Script errors are mostly likely to be caused by an error within a script that’s hosted on a different domain (for example, CDN scripts). As a result, the user’s browser stops the script from executing in order to prevent an attack called cross-site request forgery. However, it may also be a problem if the script is stored on the same domain but uses a different port, protocol (for example http:// instead of https://), or subdomain.

For example, an error within a script on another domain might look like this:

// File: https://another-domain.com/index.js
const a = {};
console.log(a.b.c);

In your domain, calling that script may look like this. This scenario will yield a script error being sent to Raygun4JS.

// File: origin-domain.com/index.html
<script src="http://another-domain.com/index.js"></script>

What is a Long Running Script?

You may also find an error similar to Script error in your Raygun dashboard, called “Long Running Script”. Their names are similar, but they are entirely different errors you need to handle differently.

While Script error is caused by violating the browser’s same-origin policy, a Long Running Script indicates performance issues. Every browser has a timeframe for script execution. If a script needs more time to execute, a Long Running Script error will occur. The user will also be presented with a dialog box where they can decide if they want to stop the script or keep waiting for their asset to load.

You can resolve the Long Running Script error by using coding best practices, modularizing your scripts, and thoroughly testing code before deployment under as many different conditions as possible.

Script error–The same-origin policy

CORS, or Cross-Origin Resource Sharing, is an official W3C recommendation that defines the mechanism of properly making cross-origin requests on the client side. The same-origin policy, enforced by modern browsers, means that scripts only have full access rights if they are loaded from the same origin domain as the original document—when the script passes the CORS validation.

With regards to the architecture of modern websites and applications, CORS and the same-origin policy present a problem. Due to the nature of HTTP 1.1, key resources (including JavaScript) are hosted on non-origin, or ’third-party’, domains. Particular to this problem, CDNs use the massive resources of public clouds to keep both costs and response times low.

When your web application code is defined and loaded to a script hosted on a different domain to the one in the address bar, errors that hit the window.onerror event handler won’t have any stack trace or message context for you to debug.

Not having a stack trace isn’t a problem when developing locally. However, no information on an error becomes a critical issue when trying to figure out why a site is breaking on a user’s machine. This is most obvious when Raygun4JS reports these errors, and the error groups lack any indication as to what happened.

Control the same-origin policy

The specification and implementation for controlling the same-origin policy is documented nicely here. You’ll notice proper implementations are only available in modern browsers. See different browser behaviors in detail at the end of the article.

There are two critical pieces of metadata to include to allow a cross-domain script to pass the CORS validation in a modern browser. You need to use the first one on the script tag you add to the HTML on the origin domain and the second one on the HTTP response sent by the third-party domain.

1. On the origin domain

As the above documentation lists, you need to use the crossorigin attribute on the appropriate script tag. By example:

// File: origin-domain.com/index.html
<script crossorigin="anonymous" src="//another-domain.com/index.js"></script>

Without the crossorigin attribute present, browsers don’t use CORS at all, meaning they assume that the requested script is hosted on the same origin and can load without security risks. However, when they recognize that the script is requested from a third-party domain, your browser will refuse to load it.

When you add the crossorigin attribute with the anonymous keyword, you notify the user’s browser that you want to use the CORS mechanism to perform a secure cross-site request. Thus, there is no exchange of user credentials via cookies or HTTP authentication when the request is sent back and forth.

2. On the third-party domain

The second critical bit of data is the presence of an HTTP header on the response from the third-party domain containing the JavaScript. In particular, the Access-Control-Allow-Origin header:

Access-Control-Allow-Origin: *

The wildcard in this example allows cross-site requests to any site. This is appropriate for a CDN where the script may be requested by any third-party domain. If this is a private host for a known set of domains, you can provide that list in place of the wildcard, for instance:

Access-Control-Allow-Origin: http://origin-domain.com

You need to implement the Access-Control-Allow-Origin response header on the third-party server where the external script is coming from. How and where to add the HTTP header depends on the type of the third-party server.

For example, here is a code example you can use on an Apache server. You can add it to the .htaccess file. It enables the third-party server to send all files using the .js extension to any external domain.

<FilesMatch "\.(js)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>

Or, if you don’t only want to add CORS support for scripts but other resources you may need, such as web fonts and CSS you can use the following code instead:

<FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|css|js)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>

For instance, here’s what I get in Chrome 66, without the third-party server sending the proper HTTP header:

origin header no access As you can see, the browser can’t load the external script, and an error message appears in the console.

After adding CORS support to the server, the Access-Control-Allow-Origin header appears in the response, the third-party script loads correctly, and the error message disappears from the console:

origin header access

If you want to add CORS support to a different server platform, there’s a great website called Enable CORS you may find helpful. It shares code examples and implementations for several different platforms such as IIS, Nginx, Tomcat, and others. Besides, larger CDN providers such as KeyCDN also frequently have support pages that explain how you can set custom HTTP headers on their platform.

Use a web proxy

Using a web proxy is an alternative method you can use to get around the problem. It can be especially useful if, for some reason, you can’t modify the HTTP header sent by the third-party server. You need to set up a proxy on your web server, then make your Ajax requests, not to the third-party domain, but to the web proxy hosted on the same domain.

The proxy server forwards the request to the third-party server, fetches the script, and sends it back to your website. Thus, the browser interprets it as a same-origin request and allows your frontend code to access the third-party script. The web proxy, in fact, functions as an intermediary for your requests from other servers.

There are some open-source CORS proxies available on the web, such as crossorigin.me or CORS Anywhere. However, these services should be only used in development. Never use an open CORS proxy on a production site, they are unreliable and may pose serious security risks.

Different browser behaviors regarding Script error

Depending on what browser your users are running, the above properties may or may not have an effect. This sums up the situation as of writing, regarding browser support for the crossorigin attribute and CORS:

  • Chrome 30+
  • Firefox 13+
  • Opera 12.50+
  • Safari (at least 6+)
  • Edge 0.10+

In these browsers, if the script tag is present, the HTTP header needs to be also present. Otherwise, the script will be blocked.

Firefox has an additional behaviour for Runtime Errors. These will be provided to window.onerror, regardless of the two properties. These aren’t a security risk. Syntax errors, however, will be blocked in both Gecko and WebKit browsers, if the crossorigin attribute is present, but the associated cross-origin domain lacks the header.

Internet Explorer <= 10

Errors will be reported with all available data in IE 10 and below. Now considered a security issue.

Internet Explorer 11+

Third-party errors won’t contain any data, and the attributes are not respected at time of writing.

Ready to blast away Script errors?

Raygun Crash Reporting surfaces your Script errors for you. See what the buzz is about and start your free 14-day trial.