Skip to main content
Error tracking captures exceptions and errors in your application automatically. See full stack traces, session context, and user impact to debug issues faster than logs alone.

How it works

Error tracking monitors your application for errors and groups them into issues:
  1. PostHog captures JavaScript errors and exceptions
  2. Errors are fingerprinted and grouped by root cause
  3. Each issue shows occurrences, affected users, and stack traces
  4. Link to session replays to see exactly what users did
  5. Assign issues to team members for resolution
Error tracking is built into PostHog. Enable it in your project settings, no separate SDK needed.

Setting up error tracking

Enable error tracking in your PostHog config:
1

Enable in PostHog

posthog.init('<ph_project_api_key>', {
  api_host: '<ph_instance_address>',
  
  // Enable error tracking
  capture_exceptions: true,
  
  // Include console logs with errors
  capture_console_errors: true
})
2

Configure stack trace parsing

Upload source maps for readable stack traces:
// webpack.config.js
module.exports = {
  devtool: 'source-map',
  
  plugins: [
    new PostHogSourceMapPlugin({
      apiKey: '<ph_project_api_key>',
      appVersion: process.env.APP_VERSION
    })
  ]
}
3

Test error capture

Trigger a test error:
// Throw a test error
posthog.capture('$exception', {
  $exception_message: 'Test error',
  $exception_type: 'TestError'
})

// Or throw naturally
throw new Error('Testing error tracking')

Error grouping

Errors are automatically grouped into issues by fingerprint:
// These errors group together (same root cause)
function loadUser(userId) {
  const user = api.getUser(userId)  // Throws if user not found
  return user.name  // TypeError: Cannot read property 'name' of undefined
}

loadUser(123)  // Error instance 1
loadUser(456)  // Error instance 2 - same issue
PostHog generates fingerprints based on:
  • Error type and message
  • Stack trace signature
  • File and line number
  • Function name
Customize fingerprinting with grouping rules if PostHog groups too broadly or narrowly.

Issue details

Each issue shows comprehensive debugging context:
Full stack trace with source locations:
TypeError: Cannot read property 'name' of undefined
  at loadUser (app.js:145:23)
  at handleClick (components/UserProfile.tsx:89:12)
  at onClick (node_modules/react-dom/index.js:4567:8)
Click any frame to:
  • See surrounding code context
  • View the full file (with source maps)
  • Jump to GitHub/GitLab (with integration)

Issue management

Track issue status and ownership:
1

Issue states

Mark issues with status:
  • Active: New or ongoing errors
  • Resolved: Fixed and deployed
  • Archived: Not important or won’t fix
  • Ignored: Suppress future occurrences
2

Assignment

Assign issues to team members:
// Issues can be assigned from the UI or API
const issue = {
  id: 'issue_123',
  assignee: '[email protected]',
  status: 'active'
}
Team members get notified when assigned.
3

External integration

Link to external issue trackers:
  • Create Jira tickets from issues
  • Link to GitHub/GitLab issues
  • Sync status between systems

Custom error tracking

Track custom exceptions:
// Track handled exceptions
try {
  await riskyOperation()
} catch (error) {
  posthog.capture('$exception', {
    $exception_message: error.message,
    $exception_type: error.name,
    $exception_stack_trace_raw: error.stack,
    // Add custom context
    operation: 'riskyOperation',
    userId: currentUser.id
  })
  
  // Handle gracefully
  showErrorMessage()
}

// Track custom error states
if (apiResponse.status === 500) {
  posthog.capture('$exception', {
    $exception_message: 'API returned 500',
    $exception_type: 'APIError',
    endpoint: apiResponse.url,
    statusCode: 500
  })
}

Error filtering

Filter out noise from error tracking:
Configure errors to ignore:
posthog.init('<ph_project_api_key>', {
  capture_exceptions: true,
  
  // Ignore specific error messages
  exception_capture_ignore: [
    'ResizeObserver loop limit exceeded',
    'Non-Error promise rejection captured'
  ],
  
  // Ignore by error type
  exception_capture_ignore_types: [
    'AbortError',
    'NetworkError'
  ]
})
Reduce volume for high-frequency errors:
posthog.init('<ph_project_api_key>', {
  capture_exceptions: true,
  
  // Capture only 10% of exceptions
  exception_capture_sampling: 0.1
})
Sampling applies after grouping, so you still see all unique issues.
Track errors differently by environment:
posthog.init('<ph_project_api_key>', {
  capture_exceptions: process.env.NODE_ENV === 'production',
  
  // Add environment context
  bootstrap: {
    distinctID: userId,
    properties: {
      environment: process.env.NODE_ENV,
      version: process.env.APP_VERSION
    }
  }
})

Source maps

Upload source maps for readable stack traces:
// webpack.config.js
const { PostHogSourceMapPlugin } = require('posthog-js/webpack')

module.exports = {
  devtool: 'hidden-source-map',
  
  plugins: [
    new PostHogSourceMapPlugin({
      apiKey: '<ph_project_api_key>',
      appVersion: require('./package.json').version,
      
      // Optional: only upload in production
      enabled: process.env.NODE_ENV === 'production'
    })
  ]
}

Common workflows

Debug production errors

  1. See error in error tracking
  2. Click to view stack trace
  3. Open linked session replay
  4. Watch what user did before error
  5. Reproduce locally and fix

Monitor error trends

Create dashboard tracking:
  • Total errors over time
  • Errors by type
  • Most affected users
  • Error rate by version

Alert on critical errors

Set up alerts for:
  • New error types
  • Error spike (10x normal rate)
  • Errors affecting >100 users
  • Payment or auth errors

Prioritize fixes

Sort issues by:
  • User impact (affected users × frequency)
  • Business criticality (checkout, payment)
  • Recent spike in occurrences
  • Unresolved age

Error alerts

Get notified when errors occur:
// Configure alerts in PostHog UI
const alert = {
  name: 'Critical error spike',
  
  // Trigger when error count increases
  trigger: {
    metric: 'error_count',
    threshold: 100,
    window: '1h',
    comparison: 'previous_period'
  },
  
  // Filter to critical errors only
  filters: {
    error_type: ['TypeError', 'ReferenceError'],
    url_contains: '/checkout'
  },
  
  // Notify via Slack or email
  notifications: ['slack', 'email']
}

Best practices

Include relevant data with exceptions:
try {
  await processPayment(orderId)
} catch (error) {
  posthog.capture('$exception', {
    $exception_message: error.message,
    // Add business context
    orderId: orderId,
    userId: currentUser.id,
    amount: order.total,
    paymentMethod: order.method
  })
  throw error
}
Don’t just track uncaught exceptions. Track errors you catch and handle:
const result = await api.fetchData()
if (result.error) {
  // Track even though we handle it
  posthog.capture('$exception', {
    $exception_message: result.error,
    $exception_type: 'APIError'
  })
  showFallbackUI()
}
Create custom error classes for better grouping:
class PaymentError extends Error {
  constructor(message, orderId) {
    super(message)
    this.name = 'PaymentError'
    this.orderId = orderId
  }
}

throw new PaymentError('Card declined', orderId)
Automatically upload source maps on deployment:
# .github/workflows/deploy.yml
- name: Upload source maps
  run: |
    npm run build
    posthog-source-maps upload \
      --api-key $POSTHOG_API_KEY \
      --app-version $GITHUB_SHA \
      ./dist

Build docs developers (and LLMs) love