Chrome Extension Error Reporting — Developer Guide

6 min read

Error Reporting and Monitoring for Chrome Extensions

Introduction

Challenges in Extension Error Monitoring

Global Error Handlers

Window.onerror for Popup and Options Pages

// popup.js or options.js
window.onerror = (message, source, lineno, colno, error) => {
  const errorReport = {
    message,
    stack: error?.stack,
    context: 'popup',
    url: window.location.href,
    timestamp: new Date().toISOString(),
  };
  // Send to error service
  reportError(errorReport);
  return false; // Let default handler run too
};

Service Worker Error Listeners

// background.js (service worker)
self.addEventListener('error', (event) => {
  const errorReport = {
    message: event.message,
    filename: event.filename,
    lineno: event.lineno,
    colno: event.colno,
    stack: event.error?.stack,
    context: 'service-worker',
    timestamp: new Date().toISOString(),
  };
  reportError(errorReport);
});

self.addEventListener('unhandledrejection', (event) => {
  const errorReport = {
    message: event.reason?.message || String(event.reason),
    stack: event.reason?.stack,
    context: 'service-worker',
    type: 'unhandled-rejection',
    timestamp: new Date().toISOString(),
  };
  reportError(errorReport);
});

Structured Error Logging

Essential Error Data to Capture

function captureErrorContext(error, context) {
  return {
    message: error.message,
    stack: error.stack,
    context, // 'background', 'popup', 'content-script'
    extensionVersion: chrome.runtime.getManifest().version,
    browserVersion: navigator.userAgent,
    timestamp: new Date().toISOString(),
    url: window.location?.href || null, // null in service worker
  };
}

Chrome.runtime.lastError Pattern

Always check for chrome.runtime.lastError in callbacks:

chrome.runtime.sendMessage({ action: 'fetchData' }, (response) => {
  if (chrome.runtime.lastError) {
    console.error('Message failed:', chrome.runtime.lastError.message);
    reportError({
      message: chrome.runtime.lastError.message,
      context: 'message-passing',
    });
    return;
  }
  // Handle response...
});

Content Script Error Isolation

Content scripts share the page’s window — errors must be carefully isolated:

// content-script.js
(function() {
  const originalOnerror = window.onerror;
  window.onerror = (msg, url, line, col, error) => {
    // Only capture extension errors, not page errors
    if (url && url.startsWith('chrome-extension://')) {
      reportError({
        message: msg,
        stack: error?.stack,
        context: 'content-script',
        line,
        column: col,
      });
    }
    // Call original handler
    if (originalOnerror) originalOnerror(msg, url, line, col, error);
    return false;
  };
})();

External Error Services

Sentry Integration

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

Sentry.init({
  dsn: 'YOUR_SENTRY_DSN',
  release: chrome.runtime.getManifest().version,
  integrations: [
    new Sentry.Integrations.GlobalHandlers({
      onerror: true,
      onunhandledrejection: true,
    }),
  ],
});

// In content scripts, wrap with isolation
Sentry.setContext('extension', {
  context: 'content-script',
  tabId: chrome.runtime.id,
});

Custom Error Endpoint

class ErrorReporter {
  constructor(endpoint) {
    this.endpoint = endpoint;
    this.queue = [];
    this.flushInterval = setInterval(() => this.flush(), 30000);
  }

  async report(errorData) {
    this.queue.push(errorData);
    if (this.queue.length >= 10) this.flush();
  }

  async flush() {
    if (this.queue.length === 0) return;
    const batch = this.queue.splice(0);
    try {
      await fetch(this.endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ errors: batch }),
      });
    } catch (e) {
      console.error('Failed to report errors:', e);
    }
  }
}

Privacy Considerations

Source Maps for Minified Code

  1. Upload source maps to error service (Sentry, Bugsnag) during build
  2. Ensure sourceMap: true in your bundler config
  3. Reference maps in production: //# sourceMappingURL=bundle.js.map

Chrome Web Store Crash Reports

Cross-References

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.