koala73/worldmonitor · warning

Notifications are blocked. Enable them in your browser setti

Error message

Notifications are blocked. Enable them in your browser settings to continue.

What it means

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.

Source

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

/**
 * 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({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY) as BufferSource,
    });
  }

  const payload = subscriptionToPayload(sub);

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Before prompting, check Notification.permission === 'denied' and show instructions to unblock (padlock icon → Site settings → Notifications → Allow) instead of calling requestPermission().
  2. After the user changes the setting, they must reload or you must re-check Notification.permission — it will read 'granted' without a prompt.
  3. Never loop requestPermission() on denial; it cannot succeed programmatically.
  4. For enterprise-managed browsers, document that notifications require a policy exception.

Example fix

// before
await subscribeToPush(userId);

// after
if (typeof Notification !== 'undefined' && Notification.permission === 'denied') {
  showNotice('Notifications are blocked for this site. Enable them in your browser site settings, then reload.');
  return;
}
await subscribeToPush(userId);
Defensive patterns

Strategy: validation

Validate before calling

if (typeof Notification !== 'undefined' && Notification.permission === 'denied') {
  showUnblockInstructions('Click the padlock → Site settings → Notifications → Allow, then reload.');
  return;
}
await subscribeToPush(expectedUserId);

Type guard

function isPushPermissionDenied(): boolean {
  return typeof Notification !== 'undefined' && Notification.permission === 'denied';
}

Try / catch

try {
  await subscribeToPush(expectedUserId);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Notifications are blocked.')) {
    showUnblockInstructions(); // sticky denial — only the user can reset it
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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