Errors

Emit Vision captures errors two ways: automatically via autoCapture, and manually via captureError(). Both flows land in the Errors tab where errors are deduplicated, grouped by fingerprint, and tracked across releases.

Automatic capture

Enable autoCapture in init() to catch unhandled errors without any additional code:

import { init } from "@emit-vision/sdk-js";
 
init({
  dsn: process.env.EMIT_VISION_DSN,
  autoCapture: {
    errors: true, // window.onerror
    unhandledRejections: true, // window.onunhandledrejection
  },
});

Automatically captured errors are marked as unhandled and shown with a distinct indicator in the dashboard.

Manual capture

Use captureError() inside try/catch blocks when you want to add context to a known failure:

import { captureError } from "@emit-vision/sdk-js";
 
async function loadUserProfile(userId: string) {
  try {
    return await api.getUser(userId);
  } catch (err) {
    captureError(
      err instanceof Error ? err : new Error("loadUserProfile failed"),
      {
        context: { userId, route: "/profile" },
        tags: { component: "ProfilePage" },
      },
    );
    return null;
  }
}

Manually captured errors are marked as handled — they appear in the errors list but in a separate bucket from unhandled crashes.

React error boundaries

React render errors don't surface through window.onerror. You need an error boundary:

import { captureError } from "@emit-vision/sdk-js";
import { Component, type ReactNode, type ErrorInfo } from "react";
 
export class ErrorBoundary extends Component<
  { children: ReactNode; fallback: ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };
 
  static getDerivedStateFromError() {
    return { hasError: true };
  }
 
  componentDidCatch(error: Error, info: ErrorInfo) {
    captureError(error, {
      context: { componentStack: info.componentStack },
    });
  }
 
  render() {
    return this.state.hasError ? this.props.fallback : this.props.children;
  }
}

How errors are grouped

Errors are deduplicated by a fingerprint derived from:

  1. The error type (TypeError, ReferenceError, etc.)
  2. The top frames of the stack trace

Two occurrences of the same error with the same stack are counted as one group, not two separate errors. You can see the occurrence count and affected users in the error detail view.

What you see in the dashboard

The Errors tab shows:

  • Error groups — deduplicated by fingerprint with occurrence count and affected user count
  • First/last seen — when the error first appeared and when it was most recently seen
  • Release — which version introduced the error (requires release to be set at init)
  • Handled vs unhandled — filterable column
  • Stack trace — full stack for each individual occurrence

Error detail

Click any error group to see:

  • Stack trace with source context
  • Properties, tags, and user context from the originating event
  • Session link if a session was active
  • Adjacent events from the same session timeline

Tips

Always pass an Error object to captureError(), not a string. Strings produce generic error groups with no stack trace, which makes deduplication and debugging much harder.

  • Set release in init() so the dashboard can show which deploy introduced an error
  • Add context with route and user action info — it appears right next to the stack trace
  • Use tags for values you want to filter on (component name, feature flag, environment)