or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/posthog-js@1.335.x

docs

index.md
tile.json

tessl/npm-posthog-js

tessl install tessl/npm-posthog-js@1.335.0

PostHog Browser JS Library is a comprehensive browser analytics and feature management SDK that enables developers to capture user events, track product analytics, manage feature flags, record session replays, and implement feedback mechanisms like surveys and conversations in web applications.

error-tracking.mddocs/reference/

Error Tracking

Error tracking allows you to capture and monitor exceptions, errors, and crashes in your application automatically or manually.

Capabilities

Capture Exception Manually

Manually captures an exception with optional additional properties.

/**
 * Manually captures an exception
 * @param error - Error object or any throwable value
 * @param additionalProperties - Additional properties to attach to the exception event
 * @returns CaptureResult if successful, undefined otherwise
 */
function captureException(
    error: unknown,
    additionalProperties?: Properties
): CaptureResult | undefined;

Usage Examples:

// Capture basic error
try {
    riskyOperation();
} catch (error) {
    posthog.captureException(error);
}

// Capture with additional context
try {
    await processPayment(amount);
} catch (error) {
    posthog.captureException(error, {
        payment_amount: amount,
        user_id: userId,
        payment_method: 'credit_card'
    });
}

// Capture custom error
const customError = new Error('Custom error message');
customError.code = 'CUSTOM_ERROR_CODE';
posthog.captureException(customError, {
    context: 'user_action',
    severity: 'high'
});

// Capture non-Error objects
posthog.captureException('String error message', {
    source: 'api_response'
});

posthog.captureException({
    message: 'Custom error object',
    details: { foo: 'bar' }
});

Start Exception Autocapture

Starts automatic capture of uncaught errors and unhandled promise rejections.

/**
 * Starts automatic exception capture
 * @param config - Configuration for autocapture behavior
 */
function startExceptionAutocapture(config?: ExceptionAutoCaptureConfig): void;

Usage Examples:

// Start with default settings (all error types)
posthog.startExceptionAutocapture();

// Customize what to capture
posthog.startExceptionAutocapture({
    capture_unhandled_errors: true,      // window.onerror
    capture_unhandled_rejections: true,   // unhandledrejection
    capture_console_errors: false         // console.error
});

// Capture only promise rejections
posthog.startExceptionAutocapture({
    capture_unhandled_errors: false,
    capture_unhandled_rejections: true,
    capture_console_errors: false
});

// Start after user consent
function onErrorTrackingConsent() {
    posthog.startExceptionAutocapture();
}

Stop Exception Autocapture

Stops automatic exception capture.

/**
 * Stops automatic exception capture
 */
function stopExceptionAutocapture(): void;

Usage Examples:

// Stop autocapture
posthog.stopExceptionAutocapture();

// Stop on user opt-out
function onUserOptOut() {
    posthog.stopExceptionAutocapture();
    localStorage.setItem('error_tracking_disabled', 'true');
}

// Stop temporarily
function pauseErrorTracking() {
    posthog.stopExceptionAutocapture();
}

function resumeErrorTracking() {
    posthog.startExceptionAutocapture();
}

Types

ExceptionAutoCaptureConfig

/**
 * Configuration for automatic exception capture
 */
interface ExceptionAutoCaptureConfig {
    /**
     * Capture uncaught errors (window.onerror)
     * @default true
     */
    capture_unhandled_errors?: boolean;

    /**
     * Capture unhandled promise rejections
     * @default true
     */
    capture_unhandled_rejections?: boolean;

    /**
     * Capture console.error calls
     * @default false
     */
    capture_console_errors?: boolean;
}

CaptureResult

/**
 * Result of capturing an exception
 */
interface CaptureResult {
    /**
     * Event name that was captured
     */
    event: string;

    /**
     * Properties that were sent with the event
     */
    properties: Record<string, any>;
}

Properties

/**
 * Object containing event properties
 */
type Properties = Record<string, Property | Property[]>;

Property

/**
 * A single property value
 */
type Property = string | number | boolean | null | undefined;

Configuration

Enable Exception Capture on Init

Configure exception capture during initialization:

// Enable with default settings
posthog.init('token', {
    capture_exceptions: true
});

// Enable with custom configuration
posthog.init('token', {
    capture_exceptions: {
        capture_unhandled_errors: true,
        capture_unhandled_rejections: true,
        capture_console_errors: true
    }
});

// Disable exception capture
posthog.init('token', {
    capture_exceptions: false
});

Best Practices

Error Context

Always include relevant context when capturing errors:

// ✅ Good: Rich context
try {
    await fetchUserData(userId);
} catch (error) {
    posthog.captureException(error, {
        user_id: userId,
        operation: 'fetch_user_data',
        api_endpoint: '/api/users',
        timestamp: Date.now(),
        retry_count: retryCount,
        network_status: navigator.onLine ? 'online' : 'offline'
    });
}

// ❌ Bad: No context
try {
    await fetchUserData(userId);
} catch (error) {
    posthog.captureException(error);
}

Error Severity Levels

Categorize errors by severity:

function captureErrorWithSeverity(error, severity, context = {}) {
    posthog.captureException(error, {
        severity: severity,
        ...context
    });
}

// Usage
try {
    criticalOperation();
} catch (error) {
    captureErrorWithSeverity(error, 'critical', {
        operation: 'payment_processing'
    });
}

try {
    nonCriticalOperation();
} catch (error) {
    captureErrorWithSeverity(error, 'warning', {
        operation: 'load_recommendations'
    });
}

Error Filtering

Filter out noise and expected errors:

// Filter known/expected errors
function shouldCaptureError(error) {
    // Don't capture user cancellations
    if (error.name === 'AbortError') {
        return false;
    }

    // Don't capture network timeouts below threshold
    if (error.code === 'ETIMEDOUT' && error.timeout < 5000) {
        return false;
    }

    // Don't capture 3rd party script errors
    if (error.filename?.includes('third-party-script')) {
        return false;
    }

    return true;
}

// Wrap captureException
function captureFilteredError(error, properties = {}) {
    if (shouldCaptureError(error)) {
        posthog.captureException(error, properties);
    }
}

// Usage
try {
    await operation();
} catch (error) {
    captureFilteredError(error, { operation: 'user_action' });
}

Link to Session Replays

Include session replay URLs with exceptions:

// Always include replay URL
try {
    dangerousOperation();
} catch (error) {
    posthog.captureException(error, {
        session_replay_url: posthog.get_session_replay_url({
            withTimestamp: true,
            timestampLookBack: 30
        }),
        session_id: posthog.get_session_id()
    });
}

// Automatic integration
function captureExceptionWithReplay(error, properties = {}) {
    posthog.captureException(error, {
        ...properties,
        session_replay_url: posthog.get_session_replay_url({
            withTimestamp: true
        }),
        session_id: posthog.get_session_id(),
        user_id: posthog.get_distinct_id()
    });
}

Error Boundaries

Integrate with React error boundaries:

class ErrorBoundary extends React.Component {
    componentDidCatch(error, errorInfo) {
        posthog.captureException(error, {
            error_boundary: true,
            component_stack: errorInfo.componentStack,
            location: window.location.pathname,
            session_replay_url: posthog.get_session_replay_url({
                withTimestamp: true
            })
        });
    }

    render() {
        if (this.state.hasError) {
            return <ErrorFallback />;
        }

        return this.props.children;
    }
}

Common Patterns

Global Error Handler

Set up comprehensive error tracking:

// Initialize error tracking
function initializeErrorTracking() {
    // Start autocapture
    posthog.startExceptionAutocapture({
        capture_unhandled_errors: true,
        capture_unhandled_rejections: true,
        capture_console_errors: false // Handle manually for filtering
    });

    // Manual console.error capture with filtering
    const originalConsoleError = console.error;
    console.error = function(...args) {
        // Filter out known warnings
        const message = args[0]?.toString() || '';
        if (!message.includes('React DevTools') &&
            !message.includes('Download the React DevTools')) {
            posthog.captureException(new Error(message), {
                source: 'console.error',
                args: args
            });
        }

        originalConsoleError.apply(console, args);
    };
}

// Call on app initialization
initializeErrorTracking();

Async Error Handling

Handle async errors consistently:

// Async wrapper with error tracking
async function withErrorTracking(fn, context = {}) {
    try {
        return await fn();
    } catch (error) {
        posthog.captureException(error, {
            ...context,
            function_name: fn.name
        });
        throw error;
    }
}

// Usage
const userData = await withErrorTracking(
    () => fetchUserData(userId),
    { operation: 'fetch_user', user_id: userId }
);

// Promise wrapper
function trackPromiseErrors(promise, context = {}) {
    return promise.catch(error => {
        posthog.captureException(error, context);
        throw error;
    });
}

// Usage
trackPromiseErrors(
    fetch('/api/data'),
    { endpoint: '/api/data' }
);

API Error Tracking

Track API errors with rich context:

class APIClient {
    async request(url, options = {}) {
        try {
            const response = await fetch(url, options);

            if (!response.ok) {
                const error = new Error(`API Error: ${response.status}`);
                error.status = response.status;
                error.url = url;
                error.method = options.method || 'GET';

                posthog.captureException(error, {
                    error_type: 'api_error',
                    status_code: response.status,
                    endpoint: url,
                    method: options.method || 'GET',
                    response_headers: Object.fromEntries(response.headers)
                });

                throw error;
            }

            return response.json();
        } catch (error) {
            if (!error.status) {
                // Network error
                posthog.captureException(error, {
                    error_type: 'network_error',
                    endpoint: url,
                    method: options.method || 'GET'
                });
            }

            throw error;
        }
    }
}

Error Aggregation

Group similar errors together:

function getErrorFingerprint(error) {
    // Create unique fingerprint for error grouping
    const message = error.message || '';
    const name = error.name || 'Error';
    const stackLine = error.stack?.split('\n')[1] || '';

    return `${name}:${message}:${stackLine}`;
}

function captureExceptionWithFingerprint(error, properties = {}) {
    const fingerprint = getErrorFingerprint(error);

    posthog.captureException(error, {
        ...properties,
        error_fingerprint: fingerprint,
        error_name: error.name,
        error_message: error.message
    });
}

// Usage
try {
    operation();
} catch (error) {
    captureExceptionWithFingerprint(error, {
        context: 'user_action'
    });
}

Performance Error Tracking

Track performance-related errors:

// Track slow operations as errors
async function trackSlowOperation(fn, threshold = 5000, context = {}) {
    const startTime = Date.now();

    try {
        const result = await fn();
        const duration = Date.now() - startTime;

        if (duration > threshold) {
            const error = new Error('Operation exceeded time threshold');
            error.duration = duration;
            error.threshold = threshold;

            posthog.captureException(error, {
                error_type: 'performance',
                duration_ms: duration,
                threshold_ms: threshold,
                ...context
            });
        }

        return result;
    } catch (error) {
        const duration = Date.now() - startTime;

        posthog.captureException(error, {
            ...context,
            duration_ms: duration
        });

        throw error;
    }
}

// Usage
await trackSlowOperation(
    () => loadLargeDataset(),
    3000,
    { operation: 'load_dataset' }
);

Error Rate Limiting

Prevent error spam:

class ErrorRateLimiter {
    constructor(maxErrors = 10, windowMs = 60000) {
        this.maxErrors = maxErrors;
        this.windowMs = windowMs;
        this.errors = [];
    }

    shouldCapture(error) {
        const now = Date.now();
        const fingerprint = this.getFingerprint(error);

        // Remove old errors outside window
        this.errors = this.errors.filter(e =>
            now - e.timestamp < this.windowMs
        );

        // Count errors with same fingerprint
        const similarErrors = this.errors.filter(e =>
            e.fingerprint === fingerprint
        );

        if (similarErrors.length >= this.maxErrors) {
            console.log(`Error rate limit exceeded for: ${fingerprint}`);
            return false;
        }

        this.errors.push({
            fingerprint: fingerprint,
            timestamp: now
        });

        return true;
    }

    getFingerprint(error) {
        return `${error.name}:${error.message}`;
    }
}

const rateLimiter = new ErrorRateLimiter();

function captureRateLimitedError(error, properties = {}) {
    if (rateLimiter.shouldCapture(error)) {
        posthog.captureException(error, properties);
    }
}

Third-Party Error Integration

Integrate with error tracking services:

// Sentry integration
import * as Sentry from '@sentry/browser';

function captureToAllServices(error, properties = {}) {
    // Capture to PostHog
    posthog.captureException(error, properties);

    // Capture to Sentry
    Sentry.captureException(error, {
        extra: properties,
        contexts: {
            posthog: {
                session_id: posthog.get_session_id(),
                distinct_id: posthog.get_distinct_id()
            }
        }
    });
}

// Bugsnag integration
import Bugsnag from '@bugsnag/js';

function captureWithBugsnag(error, properties = {}) {
    posthog.captureException(error, properties);

    Bugsnag.notify(error, (event) => {
        event.addMetadata('posthog', {
            session_id: posthog.get_session_id(),
            distinct_id: posthog.get_distinct_id(),
            ...properties
        });
    });
}

Development vs Production

Handle errors differently by environment:

function captureEnvironmentAwareError(error, properties = {}) {
    const isDevelopment = process.env.NODE_ENV === 'development';

    if (isDevelopment) {
        // In development, log to console with full details
        console.error('Error captured:', error);
        console.log('Context:', properties);

        // Optionally still capture to PostHog
        // posthog.captureException(error, properties);
    } else {
        // In production, always capture
        posthog.captureException(error, {
            ...properties,
            environment: 'production'
        });
    }
}

User Impact Tracking

Track how errors affect users:

function captureErrorWithImpact(error, impact, properties = {}) {
    posthog.captureException(error, {
        ...properties,
        user_impact: impact,
        user_id: posthog.get_distinct_id(),
        session_id: posthog.get_session_id()
    });
}

// Usage examples
try {
    await submitPayment();
} catch (error) {
    captureErrorWithImpact(error, 'high', {
        operation: 'payment',
        amount: paymentAmount
    });
    showErrorToUser('Payment failed. Please try again.');
}

try {
    await loadRecommendations();
} catch (error) {
    captureErrorWithImpact(error, 'low', {
        operation: 'recommendations'
    });
    // Silently fail, show default recommendations
}

Advanced Error Tracking Patterns

Error Categorization

// Categorize errors for better analysis
enum ErrorCategory {
    NETWORK = 'network',
    VALIDATION = 'validation',
    BUSINESS_LOGIC = 'business_logic',
    THIRD_PARTY = 'third_party',
    SECURITY = 'security',
    PERFORMANCE = 'performance'
}

function captureErrorWithCategory(
    error: Error,
    category: ErrorCategory,
    properties: Properties = {}
) {
    posthog.captureException(error, {
        ...properties,
        error_category: category,
        error_severity: getSeverity(category),
        environment: process.env.NODE_ENV
    });
}

function getSeverity(category: ErrorCategory): string {
    switch (category) {
        case ErrorCategory.SECURITY:
            return 'critical';
        case ErrorCategory.BUSINESS_LOGIC:
        case ErrorCategory.NETWORK:
            return 'high';
        case ErrorCategory.VALIDATION:
        case ErrorCategory.THIRD_PARTY:
            return 'medium';
        case ErrorCategory.PERFORMANCE:
            return 'low';
    }
}

Error Context Enrichment

// Automatically enrich all errors with context
class ErrorContextEnricher {
    captureException(error: Error, additionalProperties: Properties = {}) {
        const enrichedProperties = {
            ...additionalProperties,
            
            // Session context
            session_id: posthog.get_session_id(),
            session_replay_url: posthog.get_session_replay_url({
                withTimestamp: true,
                timestampLookBack: 30
            }),
            
            // User context
            user_id: posthog.get_distinct_id(),
            user_groups: posthog.getGroups(),
            
            // Page context
            page_url: window.location.href,
            page_path: window.location.pathname,
            page_title: document.title,
            referrer: document.referrer,
            
            // Device context
            user_agent: navigator.userAgent,
            platform: navigator.platform,
            language: navigator.language,
            screen_resolution: `${window.screen.width}x${window.screen.height}`,
            viewport_size: `${window.innerWidth}x${window.innerHeight}`,
            
            // Network context
            online: navigator.onLine,
            connection_type: (navigator as any).connection?.effectiveType,
            
            // Error context
            error_name: error.name,
            error_message: error.message,
            error_stack: error.stack,
            timestamp: new Date().toISOString()
        };
        
        return posthog.captureException(error, enrichedProperties);
    }
}

const enricher = new ErrorContextEnricher();

// Usage
try {
    riskyOperation();
} catch (error) {
    enricher.captureException(error, {
        operation: 'risky_operation',
        input_params: params
    });
}

Error Recovery Tracking

// Track error recovery attempts
class ErrorRecoveryTracker {
    async withRecovery<T>(
        operation: () => Promise<T>,
        operationName: string,
        maxRetries: number = 3
    ): Promise<T> {
        let lastError: Error | null = null;
        
        for (let attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                const result = await operation();
                
                // Track successful recovery
                if (attempt > 0) {
                    posthog.capture('error_recovered', {
                        operation: operationName,
                        attempts: attempt + 1,
                        last_error: lastError?.message
                    });
                }
                
                return result;
            } catch (error) {
                lastError = error as Error;
                
                // Track retry attempt
                posthog.capture('error_retry_attempt', {
                    operation: operationName,
                    attempt: attempt + 1,
                    max_retries: maxRetries,
                    error: lastError.message
                });
                
                // Final attempt failed
                if (attempt === maxRetries) {
                    posthog.captureException(lastError, {
                        operation: operationName,
                        attempts: attempt + 1,
                        recovery_failed: true
                    });
                    
                    throw lastError;
                }
                
                // Wait before retry (exponential backoff)
                await new Promise(resolve =>
                    setTimeout(resolve, Math.pow(2, attempt) * 1000)
                );
            }
        }
        
        throw lastError!;
    }
}

// Usage
const recovery = new ErrorRecoveryTracker();

const data = await recovery.withRecovery(
    () => fetchUserData(userId),
    'fetch_user_data',
    3
);

Source Map Integration

// Enhance errors with source maps
interface EnhancedError extends Error {
    sourceFile?: string;
    sourceLine?: number;
    sourceColumn?: number;
    originalStack?: string;
}

function captureErrorWithSourceMap(error: EnhancedError, properties: Properties = {}) {
    // Source maps are typically handled by browser dev tools
    // But you can add source location if available
    const enhancedProperties = {
        ...properties,
        source_file: error.sourceFile,
        source_line: error.sourceLine,
        source_column: error.sourceColumn,
        stack_trace: error.stack,
        original_stack: error.originalStack
    };
    
    posthog.captureException(error, enhancedProperties);
}

Error Monitoring Dashboards

Real-Time Error Dashboard

class ErrorDashboard {
    private errors: Array<{
        error: Error;
        timestamp: number;
        properties: Properties;
    }> = [];
    
    private maxErrors = 100;
    
    initialize() {
        // Listen for error events
        posthog.on('eventCaptured', (data) => {
            if (data.event === '$exception') {
                this.addError({
                    error: new Error(data.properties.$exception_message),
                    timestamp: Date.now(),
                    properties: data.properties
                });
            }
        });
    }
    
    addError(errorData: any) {
        this.errors.unshift(errorData);
        
        if (this.errors.length > this.maxErrors) {
            this.errors.pop();
        }
        
        this.updateDashboard();
    }
    
    updateDashboard() {
        const errorCounts = this.errors.reduce((acc, e) => {
            const name = e.error.name;
            acc[name] = (acc[name] || 0) + 1;
            return acc;
        }, {} as Record<string, number>);
        
        console.log('Error dashboard:', {
            total: this.errors.length,
            by_type: errorCounts,
            recent: this.errors.slice(0, 5).map(e => ({
                message: e.error.message,
                time: new Date(e.timestamp).toISOString()
            }))
        });
    }
    
    getErrors() {
        return [...this.errors];
    }
    
    clearErrors() {
        this.errors = [];
        this.updateDashboard();
    }
}

Troubleshooting

Exceptions Not Being Captured

// Debug exception capture
function debugExceptionCapture() {
    console.log('Exception autocapture:', {
        enabled: posthog.config.capture_exceptions,
        config: posthog.config.capture_exceptions
    });
    
    // Test manual capture
    try {
        throw new Error('Test error');
    } catch (error) {
        const result = posthog.captureException(error, { test: true });
        console.log('Capture result:', result);
    }
    
    // Check if events are being sent
    posthog.on('eventCaptured', (data) => {
        if (data.event === '$exception') {
            console.log('Exception event captured:', data);
        }
    });
}

Too Many Errors Being Captured

// Implement smart filtering
class SmartErrorFilter {
    private errorCounts = new Map<string, number>();
    private readonly MAX_SAME_ERROR = 10;
    private readonly WINDOW_MS = 60000;
    
    shouldCapture(error: Error): boolean {
        const fingerprint = this.getFingerprint(error);
        const count = this.errorCounts.get(fingerprint) || 0;
        
        if (count >= this.MAX_SAME_ERROR) {
            console.log('Error threshold reached:', fingerprint);
            return false;
        }
        
        this.errorCounts.set(fingerprint, count + 1);
        
        // Reset count after window
        setTimeout(() => {
            this.errorCounts.delete(fingerprint);
        }, this.WINDOW_MS);
        
        return true;
    }
    
    private getFingerprint(error: Error): string {
        return `${error.name}:${error.message}`;
    }
}

const filter = new SmartErrorFilter();

// Use in global error handler
window.addEventListener('error', (event) => {
    if (filter.shouldCapture(event.error)) {
        posthog.captureException(event.error);
    }
});

Performance Impact

// Monitor error tracking performance
class ErrorPerformanceMonitor {
    private captureTimings: number[] = [];
    
    trackCapturePerformance(error: Error, properties: Properties = {}) {
        const start = performance.now();
        
        const result = posthog.captureException(error, properties);
        
        const duration = performance.now() - start;
        this.captureTimings.push(duration);
        
        if (this.captureTimings.length > 100) {
            this.captureTimings.shift();
        }
        
        return result;
    }
    
    getAverageCaptureTime(): number {
        if (this.captureTimings.length === 0) return 0;
        
        const sum = this.captureTimings.reduce((a, b) => a + b, 0);
        return sum / this.captureTimings.length;
    }
    
    logPerformanceStats() {
        console.log('Error capture performance:', {
            avg_ms: this.getAverageCaptureTime().toFixed(2),
            count: this.captureTimings.length
        });
    }
}

See Also

  • Session Recording - Link errors to session replays
  • Sentry Integration - Integrate with Sentry
  • Events - General event capture
  • Privacy - Error data privacy considerations
  • Types - Error tracking type definitions