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. mysql-binlog日志删除

    第一种方法: mysql> show binary logs; 查看mysql bin-log日志,除了这个以外的,其它都可以使用删除. mysql> purge binary logs ...

  2. 11GR2 Oracle数据库的远程投毒VNCR方式修复

    [环境介绍] 系统环境:Solaris + Oracle 11GR2 + 单机/RAC   [背景描述] 基于集团数据库安全检查项,需要数据库的远程投毒漏洞进行修复. 根据Oracle官方提供的修复文 ...

  3. Keras实现LSTM

    一.先看一个Example 1.描述,输入为一个字母,输出为这个字母的下一个顺序字母 A->B B->C C->D 2.Code import numpy from keras.mo ...

  4. Arch Linux 记录

    2017.1.8 开始花了两天装 Arch Linux,因为是第一次安装 Arch Linux,所以过程中遇到许多问题,特记录如下. 安装过程及遇到的问题: 下载镜像,刻盘. 重启进入 LiveCD ...

  5. java Socket实例

    可以实现客户端与服务端双向通信,支持多客户端连接,客户端断开连接,服务端不会出现异常 服务端代码: package com.thinkgem.jeesite.modules.socketTest.de ...

  6. Mac环境下Vagrant的安装

    1.安装Vagrant 下载地址:https://www.vagrantup.com/downloads.html 下载好pkg包后,点击安装即可. 2.安装 VirtualBox 下载地址:http ...

  7. java 日常学习记录-反射

    Hero类 package helloworld; public class Hero { public String name; //姓名 public float hp; //血量 public ...

  8. MySQL对以特定名字开头的数据库进行授权

    对以"db_1"开头的数据库进行授权 grant all privileges on `db_1%`.* to dp_admin identified by 'password'; ...

  9. [原创] f2fs文件系统源代码分析 —— 基于3.8内核 (一)

    作者:高翔 <esxgx@163.com>本文著作权归作者所有,请在转载引用时保留原文网址. 在全文开始,首先记录f2fs被3.8主线merge的mailing list:[GIT PUL ...

  10. Go语言--基础语法笔记

    ### 换了工作,好久没有添加新文章了,本来是想更新到github上的,想想还是在博客里放着,感觉以前的文章都没有很仔细,都只是问题处理的记录, 以后想新加一些整理的笔记也好 ### 主要内容 2.1 ...