koala73/worldmonitor · error

Service worker unavailable.

Error message

Service worker unavailable.

What it means

Thrown by subscribeToPush() in src/services/push-notifications.ts when getRegistration() returns null: navigator.serviceWorker.ready threw (or the context was unsupported), meaning no active service worker registration could be obtained. Push subscriptions must be created from a service worker registration, so subscription cannot proceed. The VitePWA worker registers at '/'; if it never registers, is blocked, or its registration is corrupt/unregistered, this error results.

Source

Thrown at src/services/push-notifications.ts:133

      'Content-Type': 'application/json',
      ...(init.headers ?? {}),
      Authorization: `Bearer ${token}`,
    },
  });
}

/**
 * Ask permission (if needed), subscribe via pushManager, and register
 * the endpoint with the server. Resolves with the payload the server
 * accepted, or throws on cancel / denial / network failure.
 */
export async function subscribeToPush(expectedUserId?: string): Promise<SubscriptionPayload> {
  assertExpectedAccount(expectedUserId);
  if (!isWebPushSupported()) {
    throw new Error('Web push is not supported in this browser.');
  }
  const reg = await getRegistration();
  if (!reg) throw new Error('Service worker unavailable.');

  const perm = await Notification.requestPermission();
  if (perm !== 'granted') {
    throw new Error(
      perm === 'denied'
        ? 'Notifications are blocked. Enable them in your browser settings to continue.'
        : 'Notifications permission was not granted.',
    );
  }

  // Re-use an existing subscription if pushManager already has one
  // for this origin. Re-registering via POST keeps server state in
  // sync even when the browser has a stale subscription — this is
  // important after sign-out/sign-in, where the browser's push
  // identity persists but the Convex row does not.
  let sub = await reg.pushManager.getSubscription();
  if (!sub) {
    sub = await reg.pushManager.subscribe({

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Reload the page and check DevTools → Application → Service Workers: a worker should be 'activated and running' for this origin.
  2. If none registers, fix the VitePWA/hosting setup so the worker script is served at '/' and re-register (navigator.serviceWorker.register('/sw.js')).
  3. Clear site data (workers + caches) and reload to recover from a corrupt registration.
  4. On managed/locked-down browsers, surface an informational 'notifications unavailable' message rather than retrying.

Example fix

// before
await subscribeToPush(userId);

// after
if (!('serviceWorker' in navigator)) { return; }
await navigator.serviceWorker.register('/sw.js').catch(() => null);
const reg = await navigator.serviceWorker.ready;
if (!reg) {
  showNotice('Notifications are unavailable because the service worker could not load. Reload and try again.');
  return;
}
await subscribeToPush(userId);
Defensive patterns

Strategy: validation

Validate before calling

if (!('serviceWorker' in navigator)) return;
await navigator.serviceWorker.register('/sw.js').catch(() => null);
let reg: ServiceWorkerRegistration | undefined;
try {
  reg = await navigator.serviceWorker.ready;
} catch { reg = undefined; }
if (!reg) {
  showNotice('Notifications are unavailable: the service worker could not load. Reload and try again.');
  return;
}
await subscribeToPush(expectedUserId);

Type guard

async function hasActiveServiceWorker(): Promise<boolean> {
  if (!('serviceWorker' in navigator)) return false;
  try { return !!(await navigator.serviceWorker.getRegistration()); } catch { return false; }
}

Try / catch

try {
  await subscribeToPush(expectedUserId);
} catch (err) {
  if (err instanceof Error && err.message === 'Service worker unavailable.') {
    promptReloadWithRecoveryHint(); // re-registration or cache clear usually fixes it
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Browser privacy modes or policies that block service workers; the service worker was unregistered (e.g., devtools 'Unregister' or a failed update left no active worker); serving the app from a context where the SW script 404s (misconfigured static hosting of sw.js); security software stripping the worker script.

Common situations: Dev servers without the PWA plugin active so no worker is ever registered; hosting misroutes for /sw.js or /service-worker.js; enterprise-managed browsers with SW disabled; corrupted registrations after version churn.

Related errors


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/9654f880ff433f22. Report an issue: GitHub.