{"record":{"id":"9654f880ff433f22","repo":"koala73/worldmonitor","slug":"service-worker-unavailable","errorCode":null,"errorMessage":"Service worker unavailable.","messagePattern":"Service worker unavailable\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/push-notifications.ts","lineNumber":133,"sourceCode":"      'Content-Type': 'application/json',\n      ...(init.headers ?? {}),\n      Authorization: `Bearer ${token}`,\n    },\n  });\n}\n\n/**\n * Ask permission (if needed), subscribe via pushManager, and register\n * the endpoint with the server. Resolves with the payload the server\n * accepted, or throws on cancel / denial / network failure.\n */\nexport async function subscribeToPush(expectedUserId?: string): Promise<SubscriptionPayload> {\n  assertExpectedAccount(expectedUserId);\n  if (!isWebPushSupported()) {\n    throw new Error('Web push is not supported in this browser.');\n  }\n  const reg = await getRegistration();\n  if (!reg) throw new Error('Service worker unavailable.');\n\n  const perm = await Notification.requestPermission();\n  if (perm !== 'granted') {\n    throw new Error(\n      perm === 'denied'\n        ? 'Notifications are blocked. Enable them in your browser settings to continue.'\n        : 'Notifications permission was not granted.',\n    );\n  }\n\n  // Re-use an existing subscription if pushManager already has one\n  // for this origin. Re-registering via POST keeps server state in\n  // sync even when the browser has a stale subscription — this is\n  // important after sign-out/sign-in, where the browser's push\n  // identity persists but the Convex row does not.\n  let sub = await reg.pushManager.getSubscription();\n  if (!sub) {\n    sub = await reg.pushManager.subscribe({","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/src/services/push-notifications.ts#L115-L151","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reload the page and check DevTools → Application → Service Workers: a worker should be 'activated and running' for this origin.","If none registers, fix the VitePWA/hosting setup so the worker script is served at '/' and re-register (navigator.serviceWorker.register('/sw.js')).","Clear site data (workers + caches) and reload to recover from a corrupt registration.","On managed/locked-down browsers, surface an informational 'notifications unavailable' message rather than retrying."],"exampleFix":"// before\nawait subscribeToPush(userId);\n\n// after\nif (!('serviceWorker' in navigator)) { return; }\nawait navigator.serviceWorker.register('/sw.js').catch(() => null);\nconst reg = await navigator.serviceWorker.ready;\nif (!reg) {\n  showNotice('Notifications are unavailable because the service worker could not load. Reload and try again.');\n  return;\n}\nawait subscribeToPush(userId);","handlingStrategy":"validation","validationCode":"if (!('serviceWorker' in navigator)) return;\nawait navigator.serviceWorker.register('/sw.js').catch(() => null);\nlet reg: ServiceWorkerRegistration | undefined;\ntry {\n  reg = await navigator.serviceWorker.ready;\n} catch { reg = undefined; }\nif (!reg) {\n  showNotice('Notifications are unavailable: the service worker could not load. Reload and try again.');\n  return;\n}\nawait subscribeToPush(expectedUserId);","typeGuard":"async function hasActiveServiceWorker(): Promise<boolean> {\n  if (!('serviceWorker' in navigator)) return false;\n  try { return !!(await navigator.serviceWorker.getRegistration()); } catch { return false; }\n}","tryCatchPattern":"try {\n  await subscribeToPush(expectedUserId);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Service worker unavailable.') {\n    promptReloadWithRecoveryHint(); // re-registration or cache clear usually fixes it\n    return;\n  }\n  throw err;\n}","preventionTips":["Verify the VitePWA worker is registered (Application → Service Workers) before enabling push UI.","Ensure hosting serves the worker script at '/' correctly in every environment.","Offer a 'clear site data and reload' recovery action when the registration is corrupt."],"tags":["push-notifications","service-worker","pwa","registration"],"backgroundTag":"service-worker-registration-missing","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}