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.
Cross domain scripts
Your own scripts hosted on another domain
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>
Third-party scripts hosted on another domain
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.
Unhandled promise rejections
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
- Pass an Error object with a clear message into any
reject()
calls - 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.
Manually sent errors
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'),
});
Manually thrown errors
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}`);
Syntax errors
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:
Solution: To prevent these errors from occurring, we recommend you transpile your JavaScript code to ES5 JavaScript using a tool like Babel.
Errors in browser extensions
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:
- Error appears to have occurred on line zero of the HTML file
- The message has little/no relevance with your code
- 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:
Solution:
Set the ignore3rdPartyErrors
option to true:
rg4js('options', {
ignore3rdPartyErrors: true
});
Unhandled jQuery AJAX request failures
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:
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']
});
});