firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.86k stars 895 forks source link

Failed to execute 'subscribe' on 'PushManager': Subscription failed - no active Service Worker #7575

Open 2manoj1 opened 1 year ago

2manoj1 commented 1 year ago

Operating System

MacOS

Browser Version

All browser

Firebase SDK Version

10.2.0

Firebase SDK Product:

Messaging

Describe your project's tooling

Nextjs 13 with Page setup. Example Repo -> https://github.com/2manoj1/nextjs-firebase-example/tree/main

Describe the problem

Same problem as https://github.com/firebase/firebase-js-sdk/issues/5797 Sometimes calling messaging getToken returns the error: Failed to execute 'subscribe' on 'PushManager': Subscription failed - no active Service Worker. It seems to occur on the first page load and clear storage with unregister sw, when the user refreshes the notifications work and there's no error anymore.

Screenshot 2023-08-21 at 1 45 33 PM

Steps and code to reproduce issue

dyabol commented 2 months ago

@dyabol I'm using Flutter. I translated your code, but there still are (very rare) situations where this code ends up with a 'no service worker' issue:

Future<bool> waitServiceWorker() async {
  final swr = await web.window.navigator.serviceWorker.getRegistration().toDart;
  if (swr == null) {
    return false;
  }
  web.ServiceWorker? sw;
  if (swr!.installing != null) {
    sw = swr!.installing;
  } else if (swr!.waiting != null) {
    sw = swr!.waiting;
  } else if (swr!.active != null) {
    sw = swr!.active;
  }
  if (sw != null) {
    if (sw.state == 'activated') {
      return true;
    }
    var completer = Completer<bool>();
    sw.onstatechange.add((web.Event e) {
      if ((e.target as web.ServiceWorker).state == 'activated') {
        completer.complete(true);
      }
    }.toJS);
    await completer.future;
    await web.window.navigator.serviceWorker.ready.toDart;
    return true;
  }
  return false;
}

Can't be a problem in this line?

final swr = await web.window.navigator.serviceWorker.getRegistration()

In my code, I pass the ServiceWorkerRegistration instance obtained during registration. See https://github.com/firebase/firebase-js-sdk/issues/7693#issuecomment-2331302269

andynewman10 commented 2 months ago

In my code, I pass the ServiceWorkerRegistration instance obtained during registration. See #7693 (comment)

Are you getting different references? I'd be surprised if you are.

Flutter's Firebase implementation (Flutterfire) does not let me easily dig into the registration process.

andynewman10 commented 2 months ago

Here is one typical log output, where it succeeds after the first attempt:

Obtaining FCM token
Service worker registration is in active state
Service worker is in activated state
awaiting .ready
Calling instance.getToken, attempt #0
getToken attempt succeeded after 118826 milliseconds
FCM token obtained after 1 attempt(s)

I'll post another log once I can reproduce our present issue - which is rare.

(Note the 118 seconds delay I got here, but that's another issue. In most cases, getToken completes in under 3 seconds. Out of the maybe 1000+ (or is it 3000+) debug sessions I launched since I started to work on this project, I didn't get a delay a single time with the same Flutter code running on Android or iOS. The problem might of course come from the Flutter bridge ; or even my setup, but that's more unlikely).

andynewman10 commented 2 months ago

@dyabol I confirm your method is not bullet proof. Just got the error this morning:

Obtaining FCM token
Service worker registration is in active state
Service worker is in activated state
Calling instance.getToken, attempt #0
getToken attempt failed after 481 milliseconds
Calling instance.getToken, attempt #1
getToken attempt failed after 1 milliseconds
Calling instance.getToken, attempt #2
getToken attempt failed after 1 milliseconds
Calling instance.getToken, attempt #3
getToken attempt failed after 12 milliseconds
Calling instance.getToken, attempt #4
getToken attempt succeeded after 3608 milliseconds
FCM token obtained after 5 attempt(s)

I am trying another attempt every time the 'no active service worker' error (and no other error) is raised.

sanishmistry commented 2 months ago

Does anyone know what causes this? I have had firebase messaging working on many websites for several years, and I've just noticed this issue is now happening?

mdoumbouya117 commented 1 month ago

Make sure you are not using a truncated FCM_VAPID_KEY. Try to delete and regenerate a new one.

nick-pereira commented 1 month ago

I had a middleware to override my fetch calls to add tenantid querystring to the url. Which was causing firebaseinstallations and fcmregistrations api calls to google fail. Skipping it fixed the issue.

hoyyChoi commented 1 month ago

I faced a similar issue, where errors occurred during the process. However, I noticed that when the page was refreshed after the error, the device token was successfully received. Based on this observation, I implemented the following logic:

I handled the issue by allowing up to 3 retry attempts when an error occurred during the request. This approach helped resolve the problem quickly after the initial error.

// Wait for the service worker registration to complete
await registerServiceWorker();
const token = await retryGetDeviceToken(3); // Retry up to 3 times

// Added retry logic for getDeviceToken
async function retryGetDeviceToken(retries: number): Promise<string> {
  try {
    return await getDeviceToken();
  } catch (error) {
    if (retries === 0) {
      console.error("Maximum retry attempts exceeded:", error);
      throw error;
    } else {
      console.warn(`Retrying getDeviceToken... Remaining attempts: ${retries}`);
      return retryGetDeviceToken(retries - 1);
    }
  }
}
AbdulghaniHa commented 1 month ago

If any wonders on how to reproduce the error on Mac (Safari):

  1. Safari => Settings => Privacy => Manage website data => Remove all
  2. Safari => Settings => Website => click on your website => Remove
baigsapp commented 1 week ago

I have the same issue and I registered service worker manually in NextJs. First I have register SW in useEffect:

useEffect(() => {
    if ("serviceWorker" in navigator) {
      navigator.serviceWorker
        .register("/firebase-messaging-sw.js")
        .then((registration) => {
          console.log("Service Worker registered successfully:", registration);
        })
        .catch((err) => {
          console.error("Service Worker registration failed:", err);
        });
    }
  }, []);

Then get this register SW and pass it into getToken method:

const registration = await navigator.serviceWorker.ready;
          const currentToken = await getToken(messaging, {
            vapidKey:
              "BE6JlZIdIQYTgovJ-AAb6jbCh0TNb47ibbLcOdGiXXmToXtK46_zfRK_mLGXAkLMUHfUnbAnQ4wWlZROs_hBIH4",
            serviceWorkerRegistration: registration,
          });

Done it.

DellaBitta commented 5 days ago

Hi @baigsapp,

Could you log the registration object returned in const registration = await navigator.serviceWorker.ready; ?

Done it.

Could you clarify this? Thanks!

baigsapp commented 4 days ago

@DellaBitta Yes, of course. Below the log of registration object.

Service Worker registered successfully: 
ServiceWorkerRegistration {
installing: null, waiting: null, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: 'http://localhost:3000/', …}
active: ServiceWorker {scriptURL: 'http://localhost:3000/firebase-messaging-sw.js', state: 'activated', onstatechange: null, onerror: null}
backgroundFetch: BackgroundFetchManager {}
cookies: CookieStoreManager {}
installing: null
navigationPreload: NavigationPreloadManager {}
onupdatefound: null
paymentManager: PaymentManager {userHint: ''}
periodicSync: PeriodicSyncManager {}
pushManager: PushManager {}
scope: "http://localhost:3000/"
sync: SyncManager {}
updateViaCache: "imports"
waiting: null
[[Prototype]]: ServiceWorkerRegistration