What the heck is "Script error"?

Ben Vinegar/ May 17, 2016

If you’ve done any work with the JavaScript onerror event before, you’ve probably come across the following:

"Script error."

“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). It’s painful because even though there’s an error occurring, you don’t know what the error is, nor from which code it’s originating. And that’s the whole purpose of window.onerror – getting insight into uncaught errors in your application.

The cause: cross-origin scripts

To better understand what’s going on, consider the following example HTML document, hypothetically served from http://example.com/test:

<!doctype html>
<html>
<head>
<title>example.com/test</title>
</head>
<body>
<script src="http://another-domain.com/app.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
foo(); // call function declared in app.js
</script>
</body>
</html>

Here’s the contents of http://another-domain.com/app.js. It declares a single function, foo , whose invocation will always throw a ReferenceError.

// another-domain.com/app.js
function foo() {
bar(); // ReferenceError: bar is not a function
}

When this document is loaded in the browser and JavaScript is executed, the following is output to the console (logged via the window.onerror callback):

"Script error.", "", 0, 0, undefined

This isn’t a JavaScript bug – browsers intentionally hide errors originating from script files from different origins for security reasons. It’s to avoid a script unintentionally leaking potentially sensitive information to an onerror callback that it doesn’t control. For this reason, browsers only give window.onerror insight into errors originating from the same domain. All we know is that an error occurred – nothing else!

I’m not a bad person, really!

Despite browsers’ good intentions, there are some really good reasons why you want insight into errors thrown from scripts served from different origins:

  1. Your application JavaScript files are served from a different hostname, e.g. static.sentry.io/app.js.
  2. You are using libraries served from a community CDN, like cdnjs or Google’s Hosted Libraries.
  3. You’re working with a commercial 3rd-party JavaScript library, that is only served from external servers.

But don’t worry – getting insight into a JavaScript error served by these files just needs a few simple tweaks.

The fix: CORS attributes and headers

In order to get visibility into a JavaScript exception thrown from scripts originating from different origins, you must do two things.

1) Add a crossorigin=”anonymous” script attribute

<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>

This tells the browser that the the target file should be fetched “anonymously”. This means that no potentially user-identifying information like cookies or HTTP credentials will be transmitted by the browser to the server when requesting this file.

2) Add a Cross Origin HTTP header

Access-Control-Allow-Origin: *

CORS is short for “Cross Origin Resource Sharing”, and it’s a set of APIs (mostly HTTP headers) that dictate how files ought to be downloaded and served across origins.

By setting Access-Control-Allow-Origin: *, the server is indicating to browsers that any origin can fetch this file. Alternatively, you can restrict it to only a known origin you control, e.g.

Access-Control-Allow-Origin: https://www.example.com

Note: most community CDNs properly set an Access-Control-Allow-Origin header.

$ curl --head https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.js | \
grep -i "access-control-allow-origin" Access-Control-Allow-Origin: *

Once both of these steps have been made, any errors triggered by this script will report to window.onerror just like any regular same-domain script. So instead of “Script error”, the onerror example from the beginning will yield:

"ReferenceError: bar is not defined", "http://another-domain.com/app.js", 2, 1, [Object Error]

Boom! You’re done – ”Script error” will plague you and your team no more.

An alternative solution: try/catch

Sometimes, we’re not always in a position to be able to adjust the HTTP headers of scripts our web application is consuming. In those situations, there is an alternative approach: using try/catch.

Consider the original example again, this time with try/catch:

<!-- note: crossorigin="anonymous" intentionally absent -->
<script src="http://another-domain.com/app.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
} try {
foo(); // call function declared in app.js
} catch (e) {
console.log(e);
throw e; // intentionally re-throw (caught by window.onerror)
}
</script>

For posterity, some-domain.com/app.js once again looks like this:

// another-domain.com/app.js
function foo() {
bar(); // ReferenceError: bar is not a function
}

Running the example HTML will output the following 2 entries to the console:

=> ReferenceError: bar is not defined
at foo (http://another-domain.com/b.js:2:3)
at http://example.com/test/:15:3 => "Script error.", "", 0, 0, undefined

The first console statement – from try/catch – managed to get an error object complete with type, message, and stack trace, including file names and line numbers. The second console statement from window.onerror, once again, can only output “Script error.”

Now, does this mean you need to try/catch all of your code? Probably not – if you can easily change your HTML and specify CORS headers on your CDNs, it is preferable to do so and stick to window.onerror.

But, if you don’t control those resources, using try/catch to wrap 3rd-party code is a surefire (albeit tedious) way to get insight into errors thrown by cross-origin scripts.

Note: by default, Raven.js – Sentry’s JavaScript SDK – carefully instruments built-in methods to try to automatically wrap your code in try/catch blocks. It does this to attempt to capture error messages and stack traces from all your scripts, regardless of which origin they’re served from. It’s still recommended to set CORS attributes and headers if possible.


If this blog post helped you out, and you’d like to learn more about window.onerror, you should also check out our other blog post: Capture and report JavaScript errors with window.onerror.

【转】window.onerror跨域问题的更多相关文章

  1. window.name 跨域

    跨域的由来 JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象.但是我们常常会遇到无法避免跨域的情况,如普通文章站点(article.xxx.com)需要评论,而评论站点却在chea ...

  2. JS window.name跨域封装

    JS window.name 跨域封装 function CrossDomainName(target, agent, callback, security) { if (typeof target ...

  3. window.name跨域

    window.name? 每一个页面都有一个自己的window,而window.name是window的名字. window.name跨域原理 window对象有个name属性,该属性有个特征:即在一 ...

  4. window.returnValue跨域传值问题[转]

    主页面用window.showModalDialog的时候,如果直接打开其它系统的页面,这时候别人的页面在window.returnValue=1;这样返回值的时候,主页面是取不到返回值的,原因就是因 ...

  5. HTML5 window/iframe跨域传递消息 API

    原文地址:HTML5′s window.postMessage API 在线示例:Using HTML5's window.postMessage(请打开控制台看日志) 原文日期: 2010年09月0 ...

  6. (二)文档请求不同源之window.name跨域

    一.基本原理 window.name不是一个普通的全局变量,而是当前窗口的名字.这里要注意的是每个iframe都有包裹它的window,而这个window 是top window的子窗口,而它自然也有 ...

  7. (二)文档请求不同源之window.postMessage跨域

    一.基本原理 HTML5为了解决跨域,引入了跨文档通信API(Cross-document messaging).这个API为window对象新增了一个window.postMessage方法,允许跨 ...

  8. window.name跨域实现

    参考:window.name实现的跨域数据传输 有三个页面: a.com/app.html:应用页面. a.com/proxy.html:代理文件,一般是一个没有任何内容的html文件,需要和应用页面 ...

  9. window.name 跨域数据传输

    通过window.name可以实现跨域数据传输. 要解决的功能:  www.a.com/a.html 需要获取到 www.b.com/b.html页面内容的数据 需要3个页面 www.a.com/a. ...

随机推荐

  1. JavaScript数据类型 Math对象详解

    前言 javascript使用算术运算符实现基本的算术运算,如果要实现更加复杂的算术运算,需要通过Math对象定义的常量和函数来实现.和其他对象不同,Math只是一个静态对象,并没有Math()构造函 ...

  2. CSS部分语法1

    <!-- 第1部分 CSS规则特性 1 继承性:父元素样式可以被子元素继承,一般只能继承颜色和字体: 2 层叠性:给一个元素设置不同声明,效果会叠加: 3 优先级:给同一个元素设置相同声明,效果 ...

  3. python学习08

    python中的异常处理 1.格式 try 语句块 except else finally else 是如果try语句没有异常,就执行,否则不执行 finally 不管程序是否异常,都会执行. 2.异 ...

  4. 【codeforces 983E】NN country

    Description In the NN country, there are n cities, numbered from 1 to n, and n−1 roads, connecting t ...

  5. .net 笔试题目

    2.1.using关键字有什么用?什么是IDisposable? using可以声明namespace的引入,还可以实现非托管资源的释放,实现了IDisposiable的类在using中创建,usin ...

  6. pandas.read_csv() 报错 OSError: Initializing from file failed,报错原因分析和解决方法

    今天调用pandas读取csv文件时,突然报错“ OSError: Initializing from file failed ”,我是有点奇怪的,以前用的好好的,read_csv(path)方法不是 ...

  7. Mac环境下Redis的安装

    1.下载 官网下载地址:https://redis.io/download,选择对应的下载版本,我下载的是4.0.12 2.安装 1)下载文件解压后复制到/usr/local/目录下(快速找到路径小技 ...

  8. Access denied for user ‘root’@‘localhost’(using password: YES)的解决方法

    https://www.jb51.net/article/133836.htm 搭建服务器之后只能看见test与infomation_schema两个库 https://www.cnblogs.com ...

  9. 微信小程序开发学习(一)

    一.各种JSON配置 1.小程序配置app.json 为小程序全局配置,包括所有页面路径.界面表现.网络超时时间.底部tab等,类比APP开发中manifest配置. 2.工具配置project.co ...

  10. git私有仓库与pycharm联合使用

    文章目录 1 创建git私有仓库和pycharm的使用 1.1 克隆私有仓库到本地 1.2 使用pycharm打开 1.3 添加.gitignore文件 1.4 并将其添加到仓库 1.5 提交和推送 ...