> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser error tracking

<Note>
  Browser monitoring is a beta [AppSignal Labs](/labs) feature. The
  `@appsignal/browser` package is published under the `beta` tag on npm, and its
  configuration and API may still change between beta releases. Share feedback
  in our [Discord community](https://discord.gg/EjF6ykYx63).
</Note>

Once `init()` has run, AppSignal for Browser reports uncaught errors on its own. There is nothing to wrap and no handler to register.

It listens for two things on the page's main `window`:

* Errors your code throws and never catches, through the `error` event.
* Promises that fail with nothing to handle the failure, through the `unhandledrejection` event.

Each error is sent the moment it is caught rather than held back for later, so it still arrives if the page crashes straight afterwards.

## What is reported

* **Error name and message.** The kind of error, for example `TypeError`, and the message it came with.
* **Backtrace.** The stack trace, one line per frame. Empty if the browser gives none.
* **Action.** The route the error happened on. Errors are grouped by this.
* **Revision.** The `appVersion` you set. Used to filter by version and to find the right sourcemaps.
* **Tags.** Whatever you set with `setTags()`.
* **Breadcrumbs.** The last 25 [breadcrumbs](/browser/breadcrumbs) before the error.
* **URL and user agent.** The page the error happened on, with query parameters removed as set by your [allowlist](/browser/privacy#urls-and-query-parameters), and the browser's user agent.

Errors are reported to the `browser` [namespace](/guides/namespaces), which keeps them separate from your back-end errors and their notification settings.

## How errors are grouped

Errors group by action, which is the route template you declared with `setRouteTemplate()`, or the raw path of the page when you declared none.

Declaring a template is what stops an ID-heavy route from fragmenting into one error group per ID:

<CodeGroup>
  ```js JavaScript theme={null}
  import { setRouteTemplate } from "@appsignal/browser";

  // Every error on this route now groups under "/orders/:id"
  setRouteTemplate("/orders/:id");
  ```
</CodeGroup>

Call it on each router navigation. It is the same value [web vitals are attributed to](/browser/web-vitals#how-values-are-attributed-to-routes), so one call covers both.

## Reporting errors manually

Use `captureError()` for errors you catch yourself. It goes through the same pipeline as an uncaught error, including breadcrumbs, tags, sampling, and `beforeError`:

<CodeGroup>
  ```js JavaScript theme={null}
  import { captureError } from "@appsignal/browser";

  try {
    await submitOrder(order);
  } catch (error) {
    captureError(error, { orderId: order.id });
  }
  ```
</CodeGroup>

The second argument is optional context, reported alongside the error.

React applications can catch render errors with the [error boundary](/browser/react) instead of wrapping components by hand.

## Tags

Tags are string key-values attached to every error reported from that point on. They are what you filter and search errors by in AppSignal:

<CodeGroup>
  ```js JavaScript theme={null}
  import { setTags, clearTags } from "@appsignal/browser";

  // After authentication, once the values are known
  setTags({ plan: user.plan, org_id: user.orgId });

  // On sign-out. Tags survive a reload, so clear them explicitly
  clearTags();
  ```
</CodeGroup>

Each call adds to the tags you already set rather than replacing them. Values are turned into text, an empty value removes that tag, and you can have up to 32 tags at once, with the oldest dropped to make room. Long values are shortened to 256 bytes when they arrive. Read more in our [tagging guide](/application/tagging).

A few tag names do more than filter. Setting `country_code`, `country`, or `browser` produces [attribute distributions](/guides/tagging/attribute-distributions), which break your front-end errors down by that value, and any tag can be turned into a link with [link templates](/application/link-templates).

## Rate limits

A page stuck in a loop can throw thousands of errors a second. Two limits stop the SDK from becoming the problem it is meant to report:

* **The same error over and over.** An error counts as the same one when it has the same message and the same first line of stack trace. AppSignal reports it at most five times every 10 seconds.
* **Too many errors at once.** No more than 100 errors are reported every 10 seconds in total. This catches a page throwing many *different* errors, which the first limit would miss.

If errors go missing while a page is misbehaving, one of these two is why. To send fewer errors on purpose instead, set [`errors.sampleRate`](/browser/configuration#errorssamplerate).

## Sourcemaps

A minified bundle produces stack traces full of one-letter names and enormous line numbers. [Sourcemaps](/front-end/sourcemaps) turn those back into the file names, functions, and line numbers you wrote. The `appVersion` you set is sent with each error as its revision, which is how AppSignal knows which sourcemaps go with which build. Without it, stack traces stay minified.

If your sourcemaps are public, sitting next to your JavaScript files, there is nothing more to do. If you would rather not publish them, upload them to the [sourcemaps API endpoint](/api/sourcemaps) using the same revision.

## Errors that are not reported

<Warning>
  Some errors never reach the SDK. Most of these are browser restrictions
  rather than AppSignal limitations, and each has a workaround.
</Warning>

**Errors from scripts on another domain.** When a script loaded from somewhere else throws, the browser refuses to share the details. All your page sees is the message `Script error.`, with no stack trace and no line number. There is nothing in that to debug, so the SDK throws these away rather than filling your error list with entries you cannot tell apart. To get the real errors from scripts on a CDN, serve them with an `Access-Control-Allow-Origin` header and add `crossorigin="anonymous"` to the script tags.

**Errors inside iframes.** The SDK only listens on the page's main `window`. An iframe has a `window` of its own, so errors thrown inside one do not reach it, even when the iframe is on your own domain. To monitor an iframe, call `init()` inside it as well. An iframe from another domain cannot be monitored at all.

**Errors before `init()`.** Anything that throws before `init()` runs is missed. Import your AppSignal setup first, ahead of other libraries and your own code.

**Failed promises with no error in them.** A promise can fail with any value, not only an `Error`. If the value looks close enough to an error, AppSignal keeps its name and message, so a cancelled `fetch` still reports as `AbortError`. Anything else is written out as text so the detail survives, which means `reject({ code: 500 })` arrives readable instead of empty.

## Reviewing errors in AppSignal

The Errors view groups these into issues you can filter by version and time range, assign, and get notified about. Read more in [front-end errors in AppSignal](/browser-monitoring/errors).
