Error tracking

Uncaught errors in the browser, errors on the server, and what each report holds.

In the browser

With autoCapture.errors on (the default), the SDK listens for the window’s error and unhandledrejection events and reports each one at error level. The log message is the error’s name and first line, such as TypeError: Cannot read properties of undefined (reading 'id'), and the report carries the stack trace, where it happened, the recent breadcrumb trail and a snapshot of the page. Apperio then groups it into an issue; see Errors and issues.

A rejection with something other than an Error, such as a string, is turned into one. If the text looks like TypeError: message, that name is kept.

Script error.

Browsers hide the details of errors thrown by scripts from another origin, such as a CDN, and report only Script error. with no stack. To see the real error, serve the script with an Access-Control-Allow-Origin header and load it with the crossorigin attribute.

Errors your framework catches

Some errors never reach the window. React error boundaries and Next.js error.tsx files catch rendering errors so they can show a fallback, and the SDK doesn’t see them. Report them from the boundary; the React and Next.js guides show how.

Reporting errors yourself

For an error you catch and handle, pass it to logger.error(), or to logger.fatal() if the app can’t carry on:

TypeScript
async function submitOrder(orderId: string) {
  try {
    await processOrder(orderId);
  } catch (err) {
    const error = err instanceof Error ? err : new Error(String(err));
    logger.error('Order failed', error, { orderId });
    throw error;
  }
}

logger.captureException(error, context) does the same with a fixed message; see Logging methods. Either way, the issue is titled from the error’s name and message, not from your log message.

Error messages are not redacted

The error’s name, message and stack are sent as they are. Keep personal data and secrets out of the errors you throw. See Privacy and PII redaction.

On a server

In Node.js nothing is captured automatically. Report crashes with the process events:

TypeScript
process.on('uncaughtException', (error) => {
  logger.fatal('Uncaught exception', error);
  void logger.flush().finally(() => process.exit(1));
});

process.on('unhandledRejection', (reason) => {
  const error = reason instanceof Error ? reason : new Error(String(reason));
  logger.error('Unhandled rejection', error);
});

Awaiting flush() before exiting gives the report a chance to leave. The Node.js guide covers servers, workers and scripts.

Pattern detection

On by default. The SDK watches the errors it sends and spots two patterns:

  • recurring_error: the same message (its first 80 characters) 3 or more times within 5 minutes;
  • error_spike: 3 or more errors in the last minute, and more than three times the average per minute over the last 10 minutes.

When it spots one, it logs a warn such as [Pattern Detection] recurring_error: Card declined (the error’s message) and calls your onPatternDetected function, if you gave one. The same pattern is reported at most once every 5 minutes. Detection only sees errors from the current page or process.

TypeScript
import { Apperio } from 'apperio';

const logger = new Apperio({
  apiKey: 'your-api-key',
  projectId: 'your-project-id',
  onPatternDetected: (pattern) => {
    console.warn(`${pattern.type}: ${pattern.count} errors in ${pattern.windowMs / 1000} s`);
  },
});

Turn it off with enablePatternDetection: false.