koala73/worldmonitor · warning
Notifications are blocked. Enable them in your browser…
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.
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.
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
- 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.
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
- Authenticated account changed during push setup
- Not authenticated
- Sentry returned 403 for the issues endpoint. A…
- Service worker unavailable.
- Web push is not supported in this browser.
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/868dbc8176a2bd58.
Report an issue: GitHub.
Appendix: 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)