Service worker is registered twice
Operating System
Windows 11
Browser Version
Chrome/114
Firebase SDK Version
10.0.0
Firebase SDK Product:
Messaging
Describe your project's tooling
Plain ES6 with importmap (getting Firebase from https://esm.sh)
<script type="importmap">
{
"imports": {
"fable-library/": "https://esm.sh/[email protected]/",
"react": "https://esm.sh/[email protected]",
"react-dom": "https://esm.sh/[email protected]",
"usehooks-ts": "https://esm.sh/[email protected]?exports=useCopyToClipboard,useMediaQuery",
"firebase": "https://esm.sh/[email protected]",
"firebase/app": "https://esm.sh/[email protected]/app",
"firebase/auth": "https://esm.sh/[email protected]/auth",
"firebase/firestore": "https://esm.sh/[email protected]/firestore",
"firebase/storage": "https://esm.sh/[email protected]/storage",
"firebase/functions": "https://esm.sh/[email protected]/functions",
"firebase/messaging": "https://esm.sh/[email protected]/messaging",
"react-firehooks/auth": "https://esm.sh/[email protected]/auth",
"react-firehooks/firestore": "https://esm.sh/[email protected]/firestore",
"@iconify/react": "https://esm.sh/@iconify/[email protected]",
"react-router-dom": "https://esm.sh/[email protected]",
"use-sync-external-store/shim": "https://esm.sh/[email protected]/shim",
"react-map-gl": "https://esm.sh/[email protected]",
"react-use": "https://esm.sh/[email protected]?exports=useGeolocation",
"react-webcam": "https://esm.sh/[email protected]"
}
}
</script>
Describe the problem
My service worker is being registered twice for some reason:
I'm registering the service worker myself (with { "type": "module" }) using:
export function registerServiceWorker() {
const matchValue = navigator.serviceWorker;
if (matchValue != null) {
const serviceWorker = matchValue;
const options = { "type": "module" };
return serviceWorker.register("/firebase-messaging-sw.js", options);
}
else {
return Promise.reject(new Error("Service worker not available"));
}
}
export function getFcmToken() {
return PromiseBuilder__Run_212F1D4B(promise, PromiseBuilder__Delay_62FBFDE1(promise, () => (messaging_1().then((_arg) => {
const messaging = _arg;
return registerServiceWorker().then((_arg_1) => {
const registration = _arg_1;
return getToken(messaging, {
serviceWorkerRegistration: registration,
vapidKey: "...",
});
});
}))));
}
but when I open another SPA route (where my messaging code isn't even used), I get the
Uncaught SyntaxError: Cannot use import statement outside a module (at firebase-messaging-sw.js:1:1)
message.
This makes sense as my actual firebase-messaging-sw.js looks like:
import {initializeApp} from "https://esm.sh/[email protected]/app";
import { getMessaging, onBackgroundMessage } from 'https://esm.sh/[email protected]/messaging/sw';
const firebaseConfig = { ...};
const app = initializeApp(firebaseConfig);
// Retrieve firebase messaging
const messaging = getMessaging(app);
function onMessage(payload) {
console.log('Received background message ', payload);
const { locationId, locationName, userName } = payload.data;
const notificationTitle = "";
const notificationOptions = {
};
return self.registration.showNotification(notificationTitle, notificationOptions);
}
onBackgroundMessage(messaging, onMessage);
I'm using import syntax there, so my SW needs type: "module" to work.
Steps and code to reproduce issue
- Use
importsyntax in your service worker. - Manually register the service worker with
{ "type": "module" } - Invoke
getTokenwith that registered worker.
I'm not quite sure how to draw out the actual problem. But some other firebase code is registering the worker again without the options.
I had in my file:
import { addDoc, Timestamp, collection, getDocs } from "firebase/firestore";
import { uploadBytes, ref } from "firebase/storage";
import { useAuthIdTokenResult, useAuthState } from "react-firehooks/auth";
and that shows the behaviour. But I do not get why.
Ok, I've done some digging and I think I know where this is coming from.
I can reliably reproduce the problem the moment I start executing a httpsCallable from "firebase/functions".
I'm able to patch my browser to always use "type": "module" when the service worker is registered:
<script>
const originalRegister = navigator.serviceWorker.register;
navigator.serviceWorker.register = function (scriptURL, options) {
console.trace();
options.type = "module";
return originalRegister.call(navigator.serviceWorker, scriptURL, options);
};
</script>
The trace showed my that:
Where getUsers is invoking httpsCallable.
Hi @nojaf, thanks for reaching out. I tried replicating, but I wasn't able to reproduce the same behavior. One thing to note is that this may be a race condition on the manual registration and the SDK's auto registration. I would suggest trying to remove the block of codes for navigator.serviceWorker.register because internally the SDK runs that.
If the issue persists, please share a minimal, but complete sample of a project that I can run locally.
Hi @jbalidiong, Thanks for looking into this. I'm off on vacation now, I might eventually come back to this and try and extract a sample from my codebase.
The race condition theory is quite likely the problem.
@jbalidiong Following this guide from firebase docs https://firebase.google.com/docs/cloud-messaging/flutter/receive#web for flutter at least. It says we should manually register the sw. I expect under the hood the package for flutter is using this library, so is this documentation wrong?
It appears that flutter-messaging-sw.js gets loaded by the framework when you're debugging but doesn't when you build for release. So if you follow the instructions you get weird behavior with it loading twice while debugging. Has anyone seen it get loaded twice on release builds?
Hey @nojaf. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.
If you have more information that will help us get to the bottom of this, just add a comment!
Yeah, sorry for never reporting a proper sample that reproduces the issue. Long story short, I changed my code to not register the script myself.
I'm going to close this for lack of a repro from the original user - if anyone else has this problem and can provide a repro, please feel free to open a new issue.