Script Errors

Script errors occur in a few given unfortunate circumstances - fortunately, they usually only require some minor changes in order to accommodate issues with browser security or Javascript error handling.

For more in-depth information on Script errors, see our blog post.


Enable CORS for your cross-origin hosted scripts. The MDN documentation provides further explanation of how to implement this.

Add the appropriate Access-Control-Allow-Origin header.

IIS7 example:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
   <httpProtocol>
     <customHeaders>
       <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
   </httpProtocol>
 </system.webServer>
</configuration>

Add the crossorigin="anonymous" attribute to the <script> tag:

<script crossorigin="anonymous" src="https://anotherdomain/foo.js" ></script>

This means that error information will be exposed and therefore available to Raygun4JS.


It's possible for a Script error to occur because of unhandled promise rejections. An unhandled promise rejection occurs when an error or reject() call occurs inside a promise and there is no .catch(...) handler on the promise. The error will be captured by the unhandledrejection global event listener, if this is available in the browser.

Problem: The following example will yield a Script error if the captureUnhandledRejections option is enabled and a .catch(...) has not been added to the promise:

new Promise(function promiseHandler(resolve, reject) {
  setTimeout(() => {
    if (someVariable === 0) {
      reject();
    } else {
      resolve();
    }
  }, 1000);
});

Solution 1: Ensure error data is available

  1. Pass an Error object with a clear message into any reject() calls
  2. Add .catch() statements to every Promise

For example:

var promiseResult = new Promise(function promiseHandler(resolve, reject) {
  setTimeout(() => {
    if (someVariable === 0) {
      reject(new Error('someVariable should never be zero!'));
    } else {
      resolve();
    }
  }, 1000);
}).catch(function errorHandler(error) {
  if (error instanceof Error) {
    rg4js('send', {
      error: error,
      tags: ['handled_promise_rejection']
    });
  }
  else {
    console.error(error);
  }
});

Solution 2: Disable the capture of unhandled rejections

The Raygun4JS captureUnhandledRejections flag is set to true by default. If this is enabled it will automatically send unhandled exceptions to Raygun. For unhandledrejection event browser support, please refer to the MDN documentation.

This can be disabled by setting the captureUnhandledRejections option to false inside the Raygun configuration. This can be a good solution if there are a large number of unhandled rejections in your code (or third-party code which you have no control over) and you're only concerned with capturing error data for particular promises.

<script type="text/javascript">
rg4js('options', {
  captureUnhandledRejections: false
});
 </script>

Important note: This will mean you will have to manually catch any rejections or errors in promises if you want this data to be sent to Raygun.


If you're sending anything manually, make sure that the error you're sending is valid. Below is a working example of correctly manually sending errors.

try {
  return JSON.parse(someQuestionableString);
} catch (e) {
   rg4js('send', {
    error: e,
    tags: ["handled_exception"]
  });
}

Below are some common pitfalls that cause Script errors to be sent to Raygun.

Problem 1: A null/undefined error

rg4js('send', {
  error: null,
});

Problem 2: No message passed to the Error() constructor

rg4js('send', {
  error: new Error(),
});

Problem 3: Value passed to the error property which is not an Error object

rg4js('send', {
  error: 'Something bad happened',
});

Solution: Ensure that the error value is an Error object with a clear message

rg4js('send', {
  error: new Error('Datepicker value is in the past'),
});

When throwing errors, be sure to throw an Error object, and not a string.

Problem: Throwing a string will result in a Script error being sent to Raygun:

throw `Invalid date format: ${dateFormat}`;

Solution: Throwing an Error object will result in a useful error sent to Raygun with a stacktrace:

throw new Error(`Invalid date format: ${dateFormat}`);

If your application's JavaScript gets served to the client in a modern syntax (i.e., ES2015 or newer), this can lead to Syntax errors if a user is to view the app in a legacy browser which does not support modern standards (e.g. Internet Explorer, or older versions of other popular browsers).

A syntax error can result in an obfuscated stacktrace with a single line like so:

at ? line 0, column 0 (null:0)

It can look like this in the Raygun app:

Screenshot of a syntax error in the Raygun app.

Solution: To prevent these errors from occurring, we recommend you transpile your JavaScript code to ES5 JavaScript using a tool like Babel.


Errors which occur in browser extensions will have useful information stripped out by most browsers. The error will be caught by the global error handler and sent to Raygun. These errors can be difficult to pin down, but will generally have the following characteristics:

  1. Error appears to have occurred on line zero of the HTML file
  2. The message has little/no relevance with your code
  3. A single line stacktrace with no values (e.g., at ? line 0, column 0), but appears to have come from your website

Problem: For example, the ResizeObserver loop limit exceeded error which is a common error to occur in browser extensions:

Screenshot of a browser extension error in the Raygun app.

Solution:

Set the ignore3rdPartyErrors option to true:

rg4js('options', {
  ignore3rdPartyErrors: true
});

An unhandled failed AJAX request (e.g., network failure) can lead to an unhelpful error being thrown and caught by the global error handler.

The error can look something like this:

Screenshot of an unhandled jQuery AJAX error in the Raygun app.

Problem: The request below will fail, and the obfuscated stacktrace will be sent to Raygun:

$.ajax('https://api.example.com/', {
  method: 'GET',
  dataType: 'json'
}).done(function successHandler(response) {
  // Do something with the response...
});

Solution: To fix this, you can chain on a .fail(...) function with an error handler and then decide what to do with the errorThrown value, for example:

$.ajax('https://api.example.com/', {
  method: 'GET',
  dataType: 'json'
}).done(function successHandler(response) {
  // Do something with the response...
}).fail(function errorHandler(jqXHR, statusText, errorThrown) {
  var error = errorThrown instanceof Error ? errorThrown : new Error('Bad response from server: ' + jqXHR.responseText);
  rg4js('send', {
    error: error,
    tags: ['jquery-ajax-handled']
  });
});