Service Worker Issue - Not reactivating on new events

8,364 views
Skip to first unread message

Christian Sendler

unread,
Apr 12, 2021, 3:25:05 PM4/12/21
to Chromium Extensions
I recently build an extension with a pop-up on mv3 and I'm facing a bug related to service worker event listeners. 

Description: The extension works properly for an initial period, however after an undefined period of inactivity the service workers go inactive (this is expected per the documentation). The issue however, is that the service worker is not reactivated on a new triggering event. We are registering 6 different listeners, and none of them fire after the initial period.  

All of my event listeners are at the top-level of my background.js and that is at the root of my extension files. 

Expected behavior: The service workers are reactivated when a new event is received. 

Screenshots below include folder structure, manifest, and listeners. 


file-structure.pngevent-listeners.png
manifest.png

Cuyler Stuwe

unread,
Apr 12, 2021, 7:59:07 PM4/12/21
to Chromium Extensions, Christian Sendler
I don't have any insight into this, but I suspect that it's borked.

Did you file a bug with Chromium that I can upvote?

Cuyler Stuwe

unread,
Apr 12, 2021, 8:00:26 PM4/12/21
to Chromium Extensions, Cuyler Stuwe, Christian Sendler
That "undefined period of activity"... Does approx 5-6 mins sound about right?

Christian Sendler

unread,
Apr 12, 2021, 8:08:13 PM4/12/21
to Chromium Extensions, salem...@gmail.com, Christian Sendler
Logged it here, but thinking this was the wrong place: https://github.com/GoogleChrome/chrome-extensions-samples/issues/587

RE: period of inactivity -- It's been pretty intermittent. Never > 60 minutes, but we've see it take 5 minutes and 15 minutes alike. I'm assuming through it's just a matter of when the service worker first goes inactive. Given the listeners we're using, that doesn't happen that predictably. 

The machine that's having the issue is running 89.0.4389.116 (64-bit). 

What was your thinking on the 5-6 mins? 

Jackie Han

unread,
Apr 13, 2021, 4:30:51 AM4/13/21
to Christian Sendler, Chromium Extensions
Are there any errors displayed at chrome://extensions/ ? 
Could you add some logs in each listener with a timestamp at begin/middle/end of listener?

--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/83d0f21d-2910-4aba-a30a-18c8cabdaf7cn%40chromium.org.

Jackie Han

unread,
Apr 13, 2021, 10:51:59 AM4/13/21
to Christian Sendler, Chromium Extensions
I think of an important thing.
If you modify your code in the background service worker,  you need to reload it manually, otherwise it will not respond to any event.

Christian Sendler

unread,
Apr 13, 2021, 11:05:24 AM4/13/21
to Chromium Extensions, Jackie Han, Chromium Extensions, Christian Sendler
@Jackie - no errors there or at chrome://serviceworker-internals/. But I did notice that compared to other service workers that I have running, there are two things different in serviceworker-internals (see screenshot) -- the running status and the fetch handler existence. However, we are using service worker vs background pages which I know the other extension is using...but not sure if that accounts for this. 

In your second message the "If you modify your code in the background...." where is that in the documentation? 


serviceworker.png

Jackie Han

unread,
Apr 13, 2021, 11:15:27 AM4/13/21
to Christian Sendler, Chromium Extensions
In your second message the "If you modify your code in the background...." where is that in the documentation? 

No documents say that! This is my real experience.
When I write code in service worker for a while → do something else → forget to reload it at chrome://extensions/ . Then I find SW doesn't respond. But if I don't modify code after reloading it, everything is fine, no problem.

Jackie Han

unread,
Apr 13, 2021, 11:21:29 AM4/13/21
to Christian Sendler, Chromium Extensions
PS: for chrome extensions, you should ignore "fetch handler", that is used for normal website service worker, not used for extensions.

Christian Sendler

unread,
Apr 13, 2021, 11:25:41 AM4/13/21
to Chromium Extensions, Jackie Han, Chromium Extensions, Christian Sendler
Okay - yeah I get what you mean now, and unfortunately that's no the case here. Frustrating but it's looking like I'll have to go back to mv2 and background pages... 

I found this on SO, the linked bug report has been outstanding for months...

Erek Speed

unread,
Apr 13, 2021, 7:35:45 PM4/13/21
to Christian Sendler, Chromium Extensions, Jackie Han
One note is that the bug from that stack overflow is specific to web request listeners which your listeners do not appear to be.

It's likely that you are running into a different issue that may still be fixable.

The bug you're describing is essentially, 'service workers never activate in response to events' which would be much more severe and should be filed separately.

My guess is that your problem is probably fixable since if service workers never worked after being unloaded many more people would be complaining.

Christian Sendler

unread,
Apr 13, 2021, 9:29:30 PM4/13/21
to Chromium Extensions, Erek Speed, Chromium Extensions, Jackie Han, Christian Sendler
Thanks Erek...good point, hopefully that's the case! I'm not manually unloading the service workers anywhere and we're not dealing with any remote code. Everything is packed up with the extension. 

I've done a fair bit of debugging so far, any additional advice on where to go from here? 

Message has been deleted

Simeon Vincent

unread,
Apr 14, 2021, 5:19:02 AM4/14/21
to Chromium Extensions, Christian Sendler, Erek Speed, Chromium Extensions, Jackie Han
Just closed https://github.com/GoogleChrome/chrome-extensions-samples/issues/587 as the repo you opened it on is for extensions samples. I suspect that you meant to file it on the Chromium project's issue tracker: crbug.com. In order for the extensions team to investigate the issue you open on that tracker, it would be extremely helpful if you could put together a minimal reproduction case. Screenshots of code are not very debuggable 😉

The the serviceworker-internals screenshots you shared look normal to me. The "running status" line indicates that the service worker is, well, not currently running. You could manually start it by clicking the Start button, but your listeners should wake it. As for the "fetch handler existence" line, that just means that you don't have a fetch event listener registered (example). That's normal for extensions as there's not much reason to intercept fetch requests for your extension's origin.

Cheers,

Simeon - @dotproto
Chrome Extensions DevRel

geet mehar

unread,
Feb 1, 2022, 10:49:09 AM2/1/22
to Chromium Extensions, Simeon Vincent, Christian Sendler, Erek Speed, Chromium Extensions, Jackie Han
I am also facing this issue. I am using firebase for database and the service workers goes idle within minutes. Service worker doesn't get activated on sending message from popup to service worker. Any luck anyone on this?

I am starting to think that spending time in CE development is just a waste of time. Someone please prove me wrong.

Message has been deleted

寺田治

unread,
Feb 2, 2022, 1:08:00 AM2/2/22
to Chromium Extensions, geet...@gmail.com, Simeon Vincent, Christian Sendler, Erek Speed, Chromium Extensions, Jackie Han
 I faced the same problem.
 Inserting code like the following seems to fix the problem. 
If I didn't set the callback function, it didn't work.  

------------
//content_script.js
var wakeup = function(){
    setTimeout(function(){
        chrome.runtime.sendMessage('ping', function(){
            console.log("pong");
        });
        wakeup();
    }, 10000);
}
wakeup();
------------

2022年2月2日水曜日 0:49:09 UTC+9 geet...@gmail.com:

geet mehar

unread,
Feb 2, 2022, 4:27:26 AM2/2/22
to 寺田治, Chromium Extensions, Simeon Vincent, Christian Sendler, Erek Speed, Jackie Han
I don't mind going back to version 2 but the problem is they are not allowing new extensions to be submitted for review in webstore if its MV2.

As per the suggestion of the recursive code, that is the last thing I wanted to do but if they leave no choice then we might have to do it.
--
Regards,
Geet Mehar

ComputerWhiz

unread,
Feb 7, 2022, 9:06:02 PM2/7/22
to Chromium Extensions, geet...@gmail.com, Chromium Extensions, Simeon Vincent, Christian Sendler, Erek Speed, Jackie Han, tekka...@gmail.com
Honestly, looking at this forum, I'm shocked that Google is going ahead with the push for MV3. There are so many broken APIs and stuff that simply doesn't work like it used to.

I'm almost done migrating my extension and I've never been so scared to push a release as I have been for this one. I've tested it so much over the past month, but I'm still not confident it's not just going to explode when it's published. The whole thing about service workers being non-persistent just adds another level of uncertainty and a place where it can all go wrong.

dev holyblue

unread,
Jul 16, 2022, 3:56:48 AM7/16/22
to Chromium Extensions, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
I am also facing the same problem.

The chrome extension I created in Manifest v3 is Mouse Gesture.
Immediately after launching the Chrome Browser, the Gesture function is working normally, but after a while, the Gesture function does not work.
At that time, when I checked the details screen of the extension, Service workder was inactive.

Gesture function captures mouse movement in content script,
then sends a message to event.js using [chrome.runtime.sendMessage()] and executes the action in event.js.
When this problem occurs, chrome.runtime.sendMessage() executed in content script does not generate an error,
and event.js returns the expected responce.
So I guess Service Workder(event.js) isn't stopped and the chrome API can't be run inside event.js.

I was looking for the way to make Service Workder Active from the chrome extension.
As a result, when coding [chrome.tabs.onCreated.addListener ((tab) => {})] in event.js
I found that when I created a new tab in the Chromw Browser, the Service Workder changed from NonActive to Acitve.

So I coded the following in event.js:
- - - - - - - - - - - - - - - - - - - -
chrome.tabs.onCreated.addListener ((tab) => {
     :
     :
})
let tabCreateRemove = async () => {
    MyLoggerClass.debug("[event.js][tabCreateRemove]");
    chrome.tabs.create({active : false}, (tab)=>{
        chrome.tabs.remove(tab.id);
    });
}
chrome.runtime.onMessage.addListener( async(message, sender, sendResponse) =>{
    await tabCreateRemove();
      :
      :
}
- - - - - - - - - - - - - - - - - - - -
Then, when chrome.runtime.sendMessage () is executed from the content script, Tab will be newly created / deleted by chrome.runtime.onMessage.addListener () in event.js.
As a result, SeviceWorkder is always Active and the problem is resolved.

I think this method is a forcible solution.
Please let me know if there is a better solution.

Best Regards,
2022年2月8日火曜日 11:06:02 UTC+9 ComputerWhiz:

wOxxOm

unread,
Jul 16, 2022, 10:14:58 AM7/16/22
to Chromium Extensions, dev.ho...@gmail.com, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
>  I am also facing the same problem.

Judging by the workaround that helps you there's a very high possibility of a mistake in your code: the onMessage callback is declared with async keyword, so it returns a Promise, not a literal true value. Chrome extensions API doesn't support Promise in the returned value of onMessage callback until https://crbug.com/1185241 is fixed so it's just ignored, the port is immediately closed, and the caller receives undefined in response.

The solution is to remove the async keyword and use a standard function, from which you can call another async function that can be embedded as an IIFE:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.message === "foo") {
    (async () => {
      const bar = await foo();
      sendResponse(bar);
    })();
    return true; // keep the messaging channel open for sendResponse
  }
});

Or declare a separate async function and call it from the onMessage listener:

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.message === "foo") {
    processMessage(msg).then(sendResponse);
    return true; // keep the messaging channel open for sendResponse
  }
});

async function processMessage(msg) {
  console.log('Processing message', msg);
  // .................
  return 'foo';
}




dev holyblue

unread,
Jul 17, 2022, 7:55:52 PM7/17/22
to Chromium Extensions, wOxxOm, dev holyblue, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
Hello wOxxOm. Thanks for your reply.


> Chrome extensions API doesn't support Promise in the returned value of onMessage callback until https://crbug.com/1185241 is fixed
However, I have been able to work around this issue as expected by modifying the code as follows:
- - - - - - - - - - - - - - - - - - - -
chrome.tabs.onCreated.addListener ((tab) => {
     :
     :
})

// Define Sleep
const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));



let tabCreateRemove = async () => {

    // Create / delete tabs to change Service Workder from Inactive to Active
    const tab =  await chrome.tabs.create({active : false});
    await chrome.tabs.remove(tab.id);

    // Wait 0.05 seconds for the service worker to become Active by creating the tab.
    await _sleep(50);

    // Return a Promise instance so that the caller can await.
    return new Promise((resolve, reject) => {resolve()});

}

chrome.runtime.onMessage.addListener( async(message, sender, sendResponse) =>{
    await tabCreateRemove();
   
    // Mouse Gesture Main Process
      :

      :
}
- - - - - - - - - - - - - - - - - - - -

What I wanted to ask everyone wasn't if there was a mistake in my code.
I would like to know some best practices to avoid the problem that the service worker becomes Inactive after a while and the service worker does not work properly.
I created / deleted tabs to change the service worker from Inactive to Active.

I think this method is a forcible solution.

If anyone else has worked around this issue, please let me know.
2022年7月16日土曜日 23:14:58 UTC+9 wOxxOm:

wOxxOm

unread,
Jul 18, 2022, 1:28:42 AM7/18/22
to Chromium Extensions, dev.ho...@gmail.com, wOxxOm, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
You didn't have to repeat your workaround, it was clear the first time around that it works for you. My point was that if you fix the mistake I pointed out, your code should work correctly without the need for this workaround (assuming there are no more mistakes, of course).

dev holyblue

unread,
Jul 18, 2022, 3:53:14 AM7/18/22
to Chromium Extensions, wOxxOm, dev holyblue, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
Hello wOxxOm.

I understand that the points of your proposal are the following two points.
  1. Enclose the main process of [chrome.runtime.onMessage.addListener] in [async ()].
  2. Return true [chrome.runtime.onMessage.addListener].


> chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
>   if (request.message === "foo") {
>     (async () => {
>       const bar = await foo();
>       sendResponse(bar);
>     })();
>     return true; // keep the messaging channel open for sendResponse
>   }
> });

I have tested this method.
Unfortunately, this method was not able to change an Inactive Service Worker to Active.

Is my understanding of your proposal wrong?
Could you use this method to change an Inactive Service Worker to Active?
2022年7月18日月曜日 14:28:42 UTC+9 wOxxOm:

wOxxOm

unread,
Jul 18, 2022, 4:17:00 AM7/18/22
to Chromium Extensions, dev.ho...@gmail.com, wOxxOm, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
If the code is written correctly then the active/inactive status of the service worker doesn't matter.

The only cases when it matters are 1) a bug in Chrome with registering the worker, which doesn't affect your case, and 2) performance degradation due to multiple restarts of the service worker, currently ignored/belittled by Chromium team, which is why we need the workarounds like yours or messaging-based listed here.

dev holyblue

unread,
Jul 18, 2022, 4:45:43 AM7/18/22
to Chromium Extensions, wOxxOm, dev holyblue, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
Hello wOxxOm.

>The extension works properly for an initial period, however after an undefined period of inactivity the service workers go inactive (this is expected per the documentation). 
> The issue however, is that the service worker is not reactivated on a new triggering event. We are registering 6 different listeners, and none of them fire after the initial period.  

This thread is a discussion about the issues written in blue.
I'm facing the same problem and I'm asking everyone for a workaround.

Did you solve this  issue?

2022年7月18日月曜日 17:17:00 UTC+9 wOxxOm:

wOxxOm

unread,
Jul 18, 2022, 5:19:34 AM7/18/22
to Chromium Extensions, dev.ho...@gmail.com, wOxxOm, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
Sounds like you've encountered the same bug discussed in this thread, in which case indeed you can keep the service worker running by using workarounds to alleviate the problem. When the bug is fixed such workarounds won't be necessary. Unfortunately, AFAIK, no one knows when it will be fixed because no one in Chromium team knows how to reproduce the bug reliably and judging by the fact that there are no additional programmers in the extensions team I wouldn't be surprised if the bug isn't fixed even when MV2 is removed from Chrome.

Juraj M.

unread,
Jul 18, 2022, 5:39:48 AM7/18/22
to Chromium Extensions, wOxxOm, dev.ho...@gmail.com, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
I've been refactoring many of my extensions to MV3 over the past few months and I had this problem like 3 times and in all cases it was actually caused by bug in my code :), even though I was "sure" it's broken Chrome.
What helps is:
- more logs
- experiments - like executing code manually in the console (send message to service worker)
- fail safe mechanisms - timeouts
- asserts, for example create helper assert function that will throw when you try to register top level event handler not in the first event loop iteration

Thierry bela nanga

unread,
Aug 15, 2023, 7:29:51 PM8/15/23
to Chromium Extensions, Juraj M., wOxxOm, dev.ho...@gmail.com, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
same problem here.

I have a page which contains frames and the extension content-script is injected in all frames. it works fine for the top level frame, but fails in a sub frame.
The sub frame has a script which takes a long time to load (> 30s), then when it is done loading, the service worker is no longer available.

messages sent go unanswered.

Thierry bela nanga

unread,
Aug 16, 2023, 9:26:47 AM8/16/23
to Chromium Extensions, Thierry bela nanga, Juraj M., wOxxOm, dev.ho...@gmail.com, ComputerWhiz, geet...@gmail.com, Chromium Extensions, Simeon Vincent, chri...@hiregiraffe.com, Erek Speed, Jackie Han, tekka...@gmail.com
I can definitely reproduce the issue. 
My extension runs at document_end.
When this file fails to load, se background script is already inactive.

Screenshot 2023-08-16 092326.png

Jordi Salvat i Alabart

unread,
Aug 17, 2023, 7:29:20 AM8/17/23
to Chromium Extensions, Juraj M.
Thanks for the tips, Juraj.

Would you mind sharing some more info on this?


> create helper assert function that will throw when you try to register top level event handler not in the first event loop iteration

El dia dilluns, 18 de juliol de 2022 a les 11:39:48 UTC+2, Juraj M. va escriure:

Juraj M.

unread,
Aug 17, 2023, 7:56:37 AM8/17/23
to Chromium Extensions, Jordi Salvat i Alabart, Juraj M.
Sure thing, for example this:

// top level starts as TRUE and is set to FALSE the moment current loop finishes.
let isTopLevel = true;
Promise.resolve().then(() => isTopLevel = false);

// throws if executed later on - this helps with Manifest V3 migration!
export function assertTopLevel() {
  if (!isTopLevel) throw Error('WARNING: this is not a top level event loop');
}

Basically, you set local variable to true and then schedule a resolved promise callback that flips it to false.
And since resolved promise callback will be executed only AFTER all top-level code is done running, this will help you identify all the code that runs too late.
(well, to be completely correct, you should be able to register top-level event handler also in the microtasks queue - as long as they were scheduled in the top level event loop, so for example from already resolved promises).

To use this, you simply call "assertTopLevel()" before each event handler that you want to run at top level but you are not sure if generated code really runs it in the top level.
For example:
assertTopLevel();
browser.storage.onChanged.addListener((changes, areaName) => {

If you feel like studying low level javascript event-loop and microtasks, make sure to watch this amazing video by Jake Archibald:
https://vimeo.com/254947206

Jordi Salvat i Alabart

unread,
Aug 17, 2023, 8:44:35 AM8/17/23
to Chromium Extensions, Juraj M.
Awesome. Thanks!

El dia dijous, 17 d’agost de 2023 a les 13:56:37 UTC+2, Juraj M. va escriure:

wOxxOm

unread,
Aug 17, 2023, 10:05:05 AM8/17/23
to Chromium Extensions, Jordi Salvat i Alabart, Juraj M.
> Promise.resolve().then(() => isTopLevel = false);

FWIW, strictly speaking, this is still the same event loop task because the microtask queue is processed at the end of each task. I've just tested it in current Chrome - listeners registered inside then() are waking up the worker properly. I've checked chrome://extensions-internals and the properties of the listener are the same with and without Promise.resolve().then. I've reinstalled the extension after changing the script to ensure the internal state is not cached due to an unknown bug.

A more reliable solution might be scheduler.postTask(()=>{ isTopLevel=false; }, {priority:"user-blocking"}) in Chrome/ium 94+ and Firefox 101+ (requires enabling dom.enable_web_task_scheduling) or setTimeout(() => isTopLevel = false) otherwise, although setTimeout doesn't guarantee to run in the next task (IIRC) if your code is doing some heavy lifting.

Looks like chrome API is calling our listeners in the second task of the JS event loop, which makes sense conceptually (an "event" of the event loop is the "task") and can be tested with this code after you kill the service worker process in Chrome's built-in Task Manager:

setTimeout(console.log, 0, 'setTimeout');
scheduler.postTask(()=>console.log('scheduler'), {priority:"user-blocking"});
Promise.resolve().then(()=>console.log('promise'));
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  console.log('onMessage');
});
console.log('end');

> end
> promise
> scheduler
> setTimeout
> onMessage

Note how user-blocking scheduler beats setTimeout despite being enqueued later.
Reply all
Reply to author
Forward
0 new messages