Home

Fix this error · Browser and network

Fix: "Script error." with no stack trace

Why browsers report cross-origin errors as Script error. with no stack, and how to get the real error with crossorigin and a CORS header.

The browser hides the details of an error thrown by a script loaded from another origin. Instead of the real message, file and line, your error handler receives Script error. with an empty file name, line 0 and no error object. To get the real error, load the script with crossorigin="anonymous" and serve it with an Access-Control-Allow-Origin header.

FactDetail
ErrorScript error.
What else you getFile name empty, line 0, column 0, no error object and no stack
Where you see itwindow.onerror, error reporting tools. DevTools on your own machine shows the real error
Usual causeYour bundle on a CDN or another subdomain, or a third-party script, loaded without CORS
Usual fix<script crossorigin="anonymous"> plus Access-Control-Allow-Origin on the script's host
BugTape catches itYes, but it receives the same Script error. as any other handler until you fix the script tag

What the error means

When a classic script is loaded from another origin without CORS, the HTML standard marks its errors as muted. For an error from a muted script, the browser sets the message to Script error., the file name to an empty string, the line and column to 0, and the error object to null before it fires the window error event. This stops a page from learning private details from scripts on other sites, for example a script that throws a different error depending on whether the user is logged in there.

The browser's own console still shows the real error, because DevTools is not a page script. That is why you can see the message on your machine while your error reports only say Script error..

"Another origin" includes another subdomain and another port. Your own bundle served from https://cdn.example.com to a page on https://www.example.com counts.

Common causes

  1. Your bundle is on a CDN or asset domain without CORS. The page is on www.example.com and the scripts are on static.example.com or a CDN host.
  2. Third-party scripts. Analytics, chat widgets, ads, payment and A/B testing tags throw errors in their own code.
  3. Browser extensions inject scripts that throw.
  4. Your code throws inside a callback that a cross-origin script calls. Some browsers report errors that pass through a cross-origin script's stack as muted.
  5. JSONP or other script-tag API calls to another host.

How to fix it

Add crossorigin to the script tag and CORS to the host

Both halves are needed. The attribute makes the browser request the script with CORS, and the header lets the browser accept it.

<script src="https://cdn.example.com/app.4f2a.js" crossorigin="anonymous"></script>

# Response header on cdn.example.com
Access-Control-Allow-Origin: https://www.example.com

Access-Control-Allow-Origin: * also works for public scripts with crossorigin="anonymous", which sends no cookies. If you add the attribute but the host does not send the header, the browser blocks the script, so set up the header first.

Set it in your bundler or framework

In Next.js, crossOrigin in next.config.js adds the attribute to the <script> tags that next/script generates:

// next.config.js
module.exports = {
  crossOrigin: 'anonymous',
  assetPrefix: 'https://cdn.example.com',
};

In webpack, output.crossOriginLoading sets the attribute on chunks that webpack loads at runtime:

// webpack.config.js
module.exports = {
  output: {
    publicPath: 'https://cdn.example.com/',
    crossOriginLoading: 'anonymous',
  },
};

Vite adds crossorigin to the module scripts it writes into index.html, and module scripts are always fetched with CORS. A Vite app served from another host still needs the Access-Control-Allow-Origin header on that host.

Add the CORS header on the CDN

On nginx:

location /assets/ {
  add_header Access-Control-Allow-Origin "https://www.example.com" always;
  add_header Vary Origin always;
}

On S3 with CloudFront, add a CORS configuration to the bucket that allows GET from your site's origin, and make sure the CloudFront behaviour forwards the Origin header or uses a CORS response headers policy. Then invalidate the cache, because responses cached before the change have no header.

Serve your own code from the same origin

When the scripts are your own, serving them from the page's origin (for example /assets/ on www.example.com) makes the problem go away without any CORS setup.

Third-party scripts you do not control

You can only add crossorigin="anonymous" to a third-party tag if the vendor's server sends Access-Control-Allow-Origin. Many do. Check the response headers in DevTools before you add the attribute, or the script will stop loading. If the vendor does not send the header, you will keep getting Script error. from that script. Treat those reports as coming from the vendor and look at what the user was doing when it happened.

Wrap your own callbacks to keep the real error

For your code that runs inside a third-party script's callbacks, catch errors where they happen and report them from your own code:

thirdPartyWidget.on('submit', (data) => {
  try {
    handleSubmit(data);
  } catch (err) {
    reportError(err); // fired from your code, with the real message and stack
  }
});

How to catch it in production with BugTape

The BugTape browser SDK listens for the window error event, so it receives exactly what the browser gives every other handler. For a muted error that is Script error., no file, line 0 and no stack. BugTape reports it as JavaScript error: Script error. It cannot recover the real message. Repeats on the same page group into one issue, so many different errors from one cross-origin script on one page can sit under a single Script error. issue with a high count.

The report is still useful. It carries the replay of the page, the clicks and route changes before the error, the requests around it and the console output, which often shows which widget or step was involved. Once you add crossorigin="anonymous" and the header, new reports arrive with the real message and stack, and they become separate issues for each real error.

Use the issue's count to check the fix: after the release that adds the attribute, the Script error. issue should stop growing. Set release in init() so you can see which release each report came from. When you mark the issue fixed, a report from a newer release reopens it as a regression.

import { init } from '@bugtape/sdk';

init({ apiKey: 'YOUR_PROJECT_KEY', release: import.meta.env.VITE_COMMIT_SHA });

Errors you catch and pass to reportError(err) from your own code keep their message and stack. The SDK records nothing before init() runs. Your coding agent can read the issue over MCP (npx -y bugtape connect claude, or cursor or codex) and propose the script tag or CDN change. You review the pull request.

FAQ

Why do I see the real error in DevTools but "Script error." in my reports?

DevTools runs with the browser's privileges and shows full details for every script. Page scripts, including error reporting tools, only get what the HTML standard allows, and for cross-origin scripts without CORS that is Script error. with no details.

Does crossorigin="anonymous" send cookies?

No. anonymous sends credentials only to the same origin. use-credentials sends cookies cross-origin, and then the server must return the exact origin and Access-Control-Allow-Credentials: true. Scripts on a CDN almost never need credentials.

Can I ignore Script error.?

You can filter it out, but you will lose real errors from your own code if your bundle is on another origin. Fix the script tags first. What remains after that comes from third-party scripts or extensions, and you can decide how much attention those deserve.

Will adding crossorigin break my scripts?

It will if the script's host does not send Access-Control-Allow-Origin. The browser then refuses to run the script. Check the header in DevTools on the script's response before you add the attribute.

Sources

All guides: Fix an error.

Start free. Free plan · No credit card · Set up in minutes. Or try the live demo first.