How to Capture, Diagnose, and Fix Script Errors in Front‑End Projects
This article explains why script errors appear in front‑end pages, describes methods such as enabling CORS, using try‑catch wrappers, handling JSONP and AJAX failures, and outlines logging, statistical analysis, and monitoring techniques to locate and resolve badjs issues effectively.
Preface
In daily front‑end work, badjs is a common problem. Besides obvious errors in our own JavaScript, errors from dependent resources (external scripts, interface failures) also generate
script errormessages that are hard to locate.
Front‑end developers not only implement business features but also operate page‑quality monitoring; badjs monitoring and anomaly analysis are key tasks. This article shares methods and ideas for collecting, locating, aggregating, and analyzing
script errorincidents.
Origin of script error
Pages often host static resources (js, css, images) on third‑party CDNs or rely on external assets. When a third‑party JavaScript throws an exception, the same‑origin policy hides detailed error information and returns a generic
script error.
WebKit source code (simplified):
<code>bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL) {
KURL targetURL = completeURL(sourceURL);
if (securityOrigin()->canRequest(targetURL)) return false;
// Non‑same‑origin: replace error info with default values
errorMessage = "Script error.";
sourceURL = String();
lineNumber = 0;
return true;
}
bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, const String& sourceURL) {
EventTarget* target = errorEventTarget();
if (!target) return false;
String message = errorMessage;
int line = lineNumber;
String sourceName = sourceURL;
sanitizeScriptError(message, line, sourceName);
ASSERT(!m_inDispatchErrorEvent);
m_inDispatchErrorEvent = true;
RefPtr<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line);
target->dispatchEvent(errorEvent);
m_inDispatchErrorEvent = false;
return errorEvent->defaultPrevented();
}
</code>Common solutions
Two typical ways to obtain detailed error logs for external
script error:
1. Enable CORS (Cross‑Origin Resource Sharing)
a) Add
crossorigin="anonymous"attribute to the script tag:
<code><script src="http://domain/path/*.js" crossorigin="anonymous"></script></code>When
crossorigin="anonymous"is present, the browser fetches the script anonymously, without sending cookies or other credentials.
b) The static server must return the appropriate CORS header:
<code>Access-Control-Allow-Origin: *</code>After these two steps,
window.onerrorcan capture detailed error information from cross‑origin scripts.
2. Use try‑catch wrappers
While CORS solves many cases, large enterprises often have many domains and cannot configure CORS everywhere, and some external resources may not support it. In such cases, wrap calls to external resources with
try…catchand report errors when caught:
<code>function invoke(obj, method, args) {
try {
return obj[method].apply(this, args);
} catch (e) {
reportBadjs(e); // report the error
}
}
</code>Case studies
1) For resources we control, configure CORS headers and add
crossorigin="anonymous"to obtain full stack traces.
2) JSONP request issues:
a) Interface exception – a 302 redirect to an error page results in
script error. Since the response cannot be parsed, we report the error based on the
onloadcallback:
<code>// onload handler reports server error when callback is missing
el.onload = el.onreadystatechange = function() {
if (!cgiloadOk) {
report(cgi, 'servererror');
}
};
window.newFunction = function(rsp) {
cgiloadOk = true;
window.originFunction(rsp);
};
</code>b) Non‑standard JSON response also triggers
script error. Convert the API to return a JSON string and parse it inside a
try…catchblock, reporting the raw data on failure:
<code>let sc = document.createElement('script');
let head = document.getElementsByTagName('head')[0];
sc.setAttribute('charset', charset || 'utf-8');
sc.src = url;
head.appendChild(sc);
window.newFunction = function(text) {
// try‑catch to capture parse errors
try {
let jsonStr = JSON.parse(text);
} catch (e) {
reportBadjs(text); // report the error data
}
};
</code>JSONP only supports GET and cannot obtain HTTP status codes on error.
3) AJAX requests provide status codes and response data, allowing distinction between network errors and data‑parse errors:
<code>let xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
let str = xmlHttp.responseText;
try {
let json = JSON.parse(jsonstr);
// render data
} catch (e) {
report(jsonstr, 'data parse err'); // report raw data
}
} else if (xmlHttp.readyState == 4) {
report(cgi, xmlHttp.status); // report redirect or other status
}
};
xmlHttp.open('GET', cgi, true);
xmlHttp.send();
</code>This method can capture error data and status, but the API must support CORS.
4) When external resources cannot set CORS headers, wrap their calls with
try…catchand report exceptions.
Common jQuery/Zepto usage can be wrapped as:
<code>function myBind(obj) {
let o = {
'type': 'click', // event type
'src': '.', // selector
'fun': function() {}, // method
'isStop': true // stop propagation
};
for (let i in obj) {
o[i] = obj[i];
}
if (typeof o.src === 'string') {
o.$src = $(o.src);
}
$(o.src).off(o.type).on(o.type, function(e) {
try {
o.fun.apply(o, [e, $(this)]);
} catch (ea) {
reportBadjs(ea.stack); // report error
}
if (o.isStop) {
return false;
}
});
}
</code>Alternative analysis ideas
When external resources lack CORS headers and generate
badjsor page‑refresh errors, precise location is difficult. However, auxiliary methods can help infer the cause and page health.
1. Client‑side analysis
a) Channel proportion: Analyze the
uafield of reported
script errorlogs to see the distribution across WeChat, SQ, H5, etc. Sudden spikes in a specific channel may indicate bot traffic.
During an anomaly, the channel distribution changes dramatically, revealing the problematic source.
b) UA proportion: When a new version is released, a sudden UA dominance may indicate compatibility issues with certain browsers.
2. Combine with user‑behavior analysis
a) Record user clicks (mobile example):
<code>let eventElemArr = [];
document.body.addEventListener('touchend', function(e) {
// record tag and class
eventElemArr.push([e.target.tagName, e.target.className].join('_'));
}, false);
window.onerror = function(msg, url = '', line = '', col = '', error = '') {
let errStr = [JSON.stringify(msg), url, line, col, error, eventElemArr.join('|')].join('$');
report(errStr);
return false;
};
</code>By correlating the trace ID generated on page entry with backend logs, we can link a
badjsevent to the full request trace.
<code>// generate traceid
window.initTraceid = { bizId: '', operateId: '' };
(function(){
window.initTraceid.traceid = genTraceid(window.initTraceid);
})();
window.onerror = function(msg, url = '', line = '', col = '', error = '') {
let errStr = [JSON.stringify(msg), url, line, col, error, window.traceid || ''].join('$');
report(errStr);
return false;
};
function myRequest(url) {
url = url.addParam({ traceid: window.traceid || '' });
request(url);
}
</code>3. On‑site reproduction
3.1 Record video: Capture screen snapshots at ~25 fps using
canvasor record DOM mutations. When a
script erroroccurs, upload the cached images for later playback.
3.2 Record DOM changes via
MutationObserver, then replay the mutation log together with the error.
Log reporting, aggregation, analysis and monitoring
After covering the origin, solutions, case studies, and alternative ideas, we finally discuss log handling.
1. Log reporting
<code>// Global onerror for page exceptions
window.onerror = function(msg, url, line, col, error) {
let excludeList = ['WeixinJSBridge'];
let errStr = obj2str(msg) + (url ? ';URL:' + url : '') + (line ? ';Line:' + line : '') + (col ? ';Column:' + col : '');
for (let item of excludeList) {
if (errStr.indexOf(item) > -1) return;
}
let g = new Image();
g.src = reportUrl + errStr + '&t=' + Math.random() + (window.traceid ? '&traceid=' + window.traceid : '');
return false;
};
// Promise rejection handling
window.addEventListener('rejectionhandled', event => {
window.onerror('', '', '', '', event.reason);
});
// Vue error handling
Vue.config.errorHandler = function(err, vm, info) {
window.onerror(info, '', '', '', err);
};
</code>Server‑side collection also records IP, user info, traceid, network type, UA, timestamp, etc.
2. Log aggregation and analysis
a) Quantity view – real‑time error count with alert rules.
b) Error‑message aggregation – shows most frequent messages.
c) Detailed log view – includes channel, network, user, UA, and traceid for deep investigation.
d) Multi‑dimensional analysis (carrier, device, OS, channel, etc.) to spot patterns.
3. Error monitoring
We separate ordinary
badjsfrom
servererror. Ordinary
badjsare analyzed via logs and dashboards;
servererroralerts are routed to the responsible API owners. Each page automatically generates two keys (badjs and servererror). Alerts are configured with yellow (warning) and red (critical) levels, triggering notifications to owners.
Dashboard views show spikes of
badjsafter a release, allowing rapid rollback and fix.
When a
servererroralert fires, the reported interface information enables quick identification of the problematic API.
WecTeam
WecTeam (维C团) is the front‑end technology team of JD.com’s Jingxi business unit, focusing on front‑end engineering, web performance optimization, mini‑program and app development, serverless, multi‑platform reuse, and visual building.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.