Error Tracking
Capture, group, and analyze errors in your application.
Automatic Error Capture
With autoCapture.errors enabled (the default), the SDK automatically captures two types of errors:
- Uncaught exceptions via
window.onerror - Unhandled promise rejections via the
unhandledrejectionevent
class="syntax-comment">// Both of these are captured automatically:
class="syntax-comment">// class="syntax-number">1. Synchronous errors
function riskyFunction() {
const obj = null;
obj.property; class="syntax-comment">// TypeError - captured automatically
}
class="syntax-comment">// class="syntax-number">2. Async errors(unhandled promise rejections)
async function fetchData() {
const res = await fetch(class="syntax-string">"/api/broken-endpoint");
if (!res.ok) throw new Error(class="syntax-string">"API returned " + res.status);
class="syntax-comment">// If nothing catches this, it is captured automatically
}Manual Error Logging
For errors you catch and handle, use the Apperio.error() or Apperio.fatal() methods to log them explicitly:
class="syntax-comment">// Log a caught error with context
try {
await processPayment(order);
} catch (err) {
Apperio.error(class="syntax-string">"Payment processing failed", {
error: err,
orderId: order.id,
amount: order.total,
gateway: class="syntax-string">"stripe",
});
class="syntax-comment">// Handle the error in your UI
showErrorMessage(class="syntax-string">"Payment failed. Please try again.");
}
class="syntax-comment">// Fatal errors - use for unrecoverable situations
try {
await initializeDatabase();
} catch (err) {
Apperio.fatal(class="syntax-string">"Database initialization failed", {
error: err,
connectionString: class="syntax-string">"[REDACTED]",
});
process.exit(class="syntax-number">1);
}Error Data Schema
Each captured error includes a structured error object with the following fields:
| Field | Type | Description |
|---|---|---|
name | string | Error constructor name (TypeError, RangeError, etc.) |
message | string | Error message text |
stack | string? | Full stack trace if available |
url | string? | Source file URL where the error occurred |
lineNumber | number? | Line number in the source file |
columnNumber | number? | Column number in the source file |
{
"level": "error",
"message": "Uncaught TypeError: Cannot read properties of undefined",
"eventType": "error",
"error": {
"name": "TypeError",
"message": "Cannot read properties of undefined(reading 'map')",
"stack": "TypeError: Cannot read properties of undefined(reading 'map')\n at UserList(UserList.tsx:23:18)\n at renderWithHooks(react-dom.js:1234:22)",
"url": "https://app.example.com/static/js/main.abc123.js",
"lineNumber": 23,
"columnNumber": 18
},
"url": "https://app.example.com/users",
"userAgent": "Mozilla/5.0 ..."
}Error Grouping
The Apperio dashboard groups similar errors together based on their error message and source location. This helps you identify recurring issues and their frequency without being overwhelmed by individual occurrences.
Errors are grouped by:
- Error message pattern (dynamic values are normalized)
- Error name (TypeError, RangeError, etc.)
- Source location (file, line, column when available)
You can view unique errors via the dashboard error analysis view or the API endpoint:
GET /api/v1/:projectId/logs/unique-errorsStack Traces
Stack traces are captured from the Error object's stack property. The Apperio dashboard renders stack traces with syntax highlighting and frame collapsing for easy reading.
For manually created errors, always use the Error constructor to ensure a stack trace is generated:
class="syntax-comment">// Good - includes stack trace
Apperio.error(class="syntax-string">"Operation failed", {
error: new Error(class="syntax-string">"Invalid state"),
});
class="syntax-comment">// Less useful - no stack trace
Apperio.error(class="syntax-string">"Operation failed", {
error: { message: class="syntax-string">"Invalid state" },
});Source Maps
In production, your JavaScript is typically minified and bundled, making stack traces difficult to read. Apperio captures the raw stack trace as-is. To get readable traces:
- Generate source maps during your build process
- Upload source maps to your error tracking tool or keep them accessible for manual de-obfuscation
- Use named functions instead of anonymous arrows for clearer stack traces
class="syntax-comment">// Prefer named functions for better stack traces
function handleSubmit(event) {
class="syntax-comment">// ...
}
class="syntax-comment">// Instead of anonymous arrows
const handleSubmit = (event) => {
class="syntax-comment">// ...
};Best Practices
- Always include context -- Pass relevant data (user IDs, request params, state) with error logs to aid debugging.
- Use appropriate levels --
errorfor recoverable errors,fatalfor unrecoverable crashes. - Avoid logging PII in errors -- The sanitizer catches common patterns, but avoid including raw user input in error messages.
- Set up alert rules -- Configure alerts for new error types or error rate spikes via the dashboard.
Tip