{"record":{"id":"868dbc8176a2bd58","repo":"koala73/worldmonitor","slug":"notifications-are-blocked-enable-them-in-your-bro","errorCode":null,"errorMessage":"Notifications are blocked. Enable them in your browser settings to continue.","messagePattern":"Notifications are blocked\\. Enable them in your browser settings to continue\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/push-notifications.ts","lineNumber":139,"sourceCode":"\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({\n      userVisibleOnly: true,\n      applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY) as BufferSource,\n    });\n  }\n\n  const payload = subscriptionToPayload(sub);","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/src/services/push-notifications.ts#L121-L157","documentation":"Thrown by subscribeToPush() in src/services/push-notifications.ts when Notification.requestPermission() resolves to 'denied'. Denial is sticky: the user (or browser/enterprise policy) previously blocked notifications for this origin, and once denied, requestPermission() can never re-show the prompt — every subsequent call immediately returns 'denied'. Only the user can undo it via site permissions UI.","triggerScenarios":"User clicked 'Block' on a previous prompt; OS-level or enterprise policy denies notifications; the site's permission was reset to block in browser settings; calling requestPermission() again after a prior denial (instant 'denied', no prompt).","commonSituations":"Users who dismissed early prompts with Block and later try to enable notifications from settings; corporate-managed browser profiles with notifications disabled; kiosk/restricted profiles.","solutions":["Before prompting, check Notification.permission === 'denied' and show instructions to unblock (padlock icon → Site settings → Notifications → Allow) instead of calling requestPermission().","After the user changes the setting, they must reload or you must re-check Notification.permission — it will read 'granted' without a prompt.","Never loop requestPermission() on denial; it cannot succeed programmatically.","For enterprise-managed browsers, document that notifications require a policy exception."],"exampleFix":"// before\nawait subscribeToPush(userId);\n\n// after\nif (typeof Notification !== 'undefined' && Notification.permission === 'denied') {\n  showNotice('Notifications are blocked for this site. Enable them in your browser site settings, then reload.');\n  return;\n}\nawait subscribeToPush(userId);","handlingStrategy":"validation","validationCode":"if (typeof Notification !== 'undefined' && Notification.permission === 'denied') {\n  showUnblockInstructions('Click the padlock → Site settings → Notifications → Allow, then reload.');\n  return;\n}\nawait subscribeToPush(expectedUserId);","typeGuard":"function isPushPermissionDenied(): boolean {\n  return typeof Notification !== 'undefined' && Notification.permission === 'denied';\n}","tryCatchPattern":"try {\n  await subscribeToPush(expectedUserId);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Notifications are blocked.')) {\n    showUnblockInstructions(); // sticky denial — only the user can reset it\n    return;\n  }\n  throw err;\n}","preventionTips":["Check Notification.permission before prompting; a denied state can never be changed programmatically.","Show step-by-step unblock instructions keyed to the browser instead of re-calling requestPermission().","After the user changes the setting, require a reload and re-check permission before retrying subscribe."],"tags":["push-notifications","permissions","notification-api","user-action-required"],"backgroundTag":"permission-denied","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"}