error.page docs

Browser SDK reference

Every init() option, identify(), track(), captureException(), when the branded page is shown, session replay, and troubleshooting.

error.page SDK Integration Guide

The browser SDK is a single vanilla-JS file, about 13 KB gzipped, with zero dependencies. It detects errors, shows your branded page, reports the incident (with a stack trace), and clears the page automatically when you recover.

Installation

Zero-install (recommended)

Add two lines to your page <head> — no configuration:

<meta name="error-page-key" content="ep_live_your_project_api_key_here">
<script src="https://error.page/sdk.js"></script>

The loader reads the meta tag, fetches your project config (colours, copy, CTA) from the API, then initialises the SDK with everything pre-filled.

Manual install

Include the SDK before your application code and call init():

<script src="https://error.page/js/errorpage.js"></script>
<script>
  ErrorPage.init({
    projectId: 'ep_your_project_api_key',   // your project's public API key
    slug: 'your-project-slug'               // used for branded config + status
  });
</script>

What it captures

Once initialised, the SDK automatically:

  • Catches uncaught exceptions (window.onerror) and unhandled promise rejections — reported as js_crash, with the JavaScript stack trace.
  • Intercepts fetch() and XMLHttpRequest responses with HTTP 5xx — reported as server_error.
  • Intercepts network failures (a failed fetch()/XHR) — reported as network.
  • Sends each event to POST /api/v2/ingest using your project key.
  • Shows your branded error overlay to the user.
  • Polls for recovery and clears the page automatically (see Auto-resolve).

Stack traces

Crash reports include the JavaScript stack, shown on the incident with your app code highlighted. Set a release so you can see which build an error occurred in.

Configuration options

Every option except projectId is optional.

ErrorPage.init({
  // Required — your project's public API key (sent as X-Project-Key).
  projectId: 'ep_your_project_api_key',

  // Project slug — used to fetch branded config and poll recovery status.
  // Defaults to projectId if omitted.
  slug: 'your-project-slug',

  // API base URL. Defaults to https://error.page.
  apiBase: 'https://error.page',

  // Release/version tag (e.g. '1.4.2' or a git sha). Sent with every event so
  // you can see which build an error occurred in.
  release: '1.4.2',

  // Recovery polling. Set autoResolve:false to disable; pollInterval is in ms.
  autoResolve: true,
  pollInterval: 30000,

  // Set overlay:false to report silently without showing the branded page.
  overlay: true,

  // When the branded page is shown. By default only when YOUR site is down: a
  // 5xx or no response from your own backend (same host or domain), or an
  // uncaught crash in your own code. 4xx responses, third-party APIs,
  // third-party scripts and errors you report yourself never take over the
  // page. overlayHosts adds other backends you own; overlayOn replaces the
  // default list of error types and skips the host check.
  overlayHosts: ['api.yourcompany.com'],
  // overlayOn: ['server_error', 'network', 'js_crash'],

  // Full visual session replay (a DOM recording, inputs masked by default).
  // Off unless you opt in; the masking policy is set per project in Settings.
  sessionReplay: true,

  // Ignore noise you don't want filed as incidents (substrings or RegExps).
  ignoreErrors: ['ResizeObserver loop', /ChunkLoadError/],

  // Capture request/response bodies on failed calls. Scrubbed server-side.
  capturePayloads: true,

  // Hardened ingest (optional): a short-lived signed token, or a function
  // returning one (may return a Promise). Sent as Authorization: Bearer.
  // See the Hardened ingest guide.
  getToken: function () { return fetchMyIngestToken(); },

  // Optional lifecycle callbacks.
  onError: function (type, message) { /* ... */ },
  onResolve: function () { /* ... */ },
});

Branding (header, body, CTA, colours) is configured per project in the dashboard and fetched at runtime — you don't set it in code. To skip that round-trip you may pass a branded object with the same shape the API returns, but the dashboard is the source of truth.

Reporting errors you caught

Uncaught exceptions are reported automatically. For errors your code catches (a try/catch, a promise .catch, a React error boundary), report them yourself. They're filed as incidents but never show the branded page — your app is handling them.

try {
  await checkout(cart);
} catch (err) {
  ErrorPage.captureException(err, { component: 'checkout', items: cart.length });
}

ErrorPage.captureMessage('Inventory sync returned no rows', { job: 'inventory' });

Error types

The SDK classifies each event as one of:

Type When
js_crash Uncaught exceptions and unhandled promise rejections
server_error HTTP 5xx from a fetch()/XHR response
network A failed fetch()/XHR (connection error, DNS, CORS, timeout)

Auto-resolve behaviour

When autoResolve is on (the default):

  1. After an error is shown, the SDK polls GET /api/v2/projects/{slug}/status every pollInterval ms (30 s default).
  2. After 3 consecutive healthy responses, the overlay is dismissed and a green banner slides in: "✓ Everything's working again" — with a continue → link to your configured CTA URL when set.
  3. The banner auto-dismisses after 30 seconds.

This is the client side of recovery. Incidents are also resolved server-side (manually, on release, or by Business-tier auto-resolve) — see the Incident feed and Integrations guides.

Hardened ingest

The project key ships in the browser, so it's public by design; rate limits and optional origin allowlists bound abuse. For stricter control, enable hardened ingest and supply a short-lived signed token via token or getToken. See the Hardened ingest guide for the full ladder from public key → secret key → signed tokens.

Browser compatibility

  • Modern browsers (ES6+); no polyfills needed — all native browser APIs.
  • Works alongside any framework. Load the SDK before your app code so it's installed in time to catch early errors.

Example: e-commerce checkout

<script src="https://error.page/js/errorpage.js"></script>
<script>
  ErrorPage.init({
    projectId: 'ep_live_xK3mP...',
    slug: 'acme-store',
    release: window.APP_VERSION || '0.0.0',
    onResolve: function () { console.log('checkout recovered'); },
  });
</script>

Tips

  • Load early — before React/Vue/etc. so the SDK catches boot-time errors.
  • Set release so you can see which build an error occurred in.
  • Test with a staging project key before pointing production at it.
  • Design your branded page in the dashboard — colours, copy and CTA are fetched at runtime, no code change needed to update them.

Troubleshooting: "Script error."

If an incident's message is just Script error., the browser deliberately withheld the details. When a script loaded from another origin throws, and it was loaded without crossorigin="anonymous" (or the server didn't send Access-Control-Allow-Origin), the browser reports only that string — no message, stack, file or line — to stop one site reading errors out of another.

The SDK makes this actionable. A sanitised error is reported as Script error. (cross-origin script withheld details) with:

  • metadata.suspects — the scripts on the page that can be the source: every <script src> from another origin that lacks a crossorigin attribute (same-origin and CORS-loaded scripts always report in full, so they're excluded). Usually that's a short list.
  • metadata.hint — how to unmask it.

To see the real error, load the suspect script with crossorigin="anonymous" and make sure its server sends Access-Control-Allow-Origin (a * is fine for public scripts). Both are required: crossorigin without the header blocks the script from loading at all.

The replay recorder loads itself from /sdk/…, which serves our bundles with those headers, using crossorigin="anonymous" — so an error inside error.page's own recorder is recognised as ours and is never filed as your site's crash. The core SDK stays on the static /js/errorpage.js path (fast and CDN-friendly); if you'd rather see full details for failures inside the SDK itself, load it from https://error.page/sdk/errorpage.js with crossorigin="anonymous" instead.

Masking what replay records

Session replay masks every form input by default (password fields can never be captured) and you can keep anything else out of a recording with three classes on your own elements:

  • ep-mask — the element's text is replaced with asterisks of the same length.
  • ep-block — the element is replaced by a blank box of the same size (use this for API keys, account numbers, anything secret).
  • ep-ignore — the input's events are not recorded at all.

Recordings are kept for your plan's retention period and then deleted.