Chrome Extension Service Worker Debugging — Developer Guide

4 min read

Service Worker Debugging Guide

Overview

Accessing SW DevTools

Common Issues

SW Goes Inactive

Lost State

Event Listeners Not Firing

Port Disconnected

Debugging Tools

chrome://serviceworker-internals

console.log Strategies

Breakpoints

Network Tab

Testing SW Lifecycle

Code Examples

Debug Logging Wrapper for SW

const DEBUG = true;
function debug(...args) {
  if (DEBUG) {
    console.log(`[SW:${Date.now()}]`, ...args);
  }
}

// Log SW startup
debug('Service worker started', { version: chrome.runtime.getManifest().version });

// Log event registration
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  debug('Message received', { message, sender });
  // handler code
});

SW Restart Detection Pattern

let isFirstRun = true;

chrome.runtime.onStartup.addListener(() => {
  isFirstRun = true;
  console.log('[SW] New session started');
});

// Log whenever SW starts (including after restarts)
console.log('[SW] Initializing, firstRun:', isFirstRun);

// Re-initialize state on each startup
async function initializeState() {
  const stored = await chrome.storage.local.get('state');
  if (!stored.state) {
    await chrome.storage.local.set({ state: { initialized: Date.now() } });
  }
  console.log('[SW] State initialized');
}

initializeState();

Connection Recovery Pattern

// For long-lived connections that may drop when SW idles
function createReconnectHandler(port) {
  port.onDisconnect.addListener(() => {
    console.log('[SW] Port disconnected, attempting reconnect...');
    // Wait a bit then reconnect
    setTimeout(() => {
      const newPort = chrome.runtime.connect({ name: 'recovered' });
      setupPortListeners(newPort);
    }, 1000);
  });
}

// Alternative: use sendMessage instead of long-lived ports
async function sendWithRetry(message, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await chrome.runtime.sendMessage(message);
    } catch (e) {
      if (i === retries - 1) throw e;
      await new Promise(r => setTimeout(r, 500));
    }
  }
}

Cross-references

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