Content script fails to send message to non-persistent background script

I’ve written an extension with a non-persistent background script (using manifest v3 without specifying persistent=false explicitly as this should be the default for v3). There is a content script that tries to send a message to the background script when the user clicks on a link by calling browser.runtime.sendMessage(...). The background script installs an event handler like this:

browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
    // ...
})

After a while, when clicking on a link for the first time, sending the message to the background script fails with “Error: Could not establish connection. Receiving end does not exist.”. It works when clicking a second time. As I understand it, this should work even when the background script has been unloaded, at least according to https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Background_scripts#add_listeners :

Defining relevant events enables background scripts to lie dormant until those events are fired and prevents the extension from missing essential triggers. Listeners must be registered synchronously from the start of the page.

Moreover, it usually works for a while, even with the background script being unloaded. It takes a while for the described behavior to manifest, and as soon as it does, it happens on every click.

Of course, I could catch the error, wait a bit, and retry - but I’d like to know what’s the recommended approach here.

Thanks, any hint would be much appreciated,
Philipp

Hey Phillip,

I just tried to reproduce the issue you described without success. Here’s the minimal extension I was using to test.

manifest.json

{
  "name": "Event page",
  "version": "0.1",
  "manifest_version": 3,
  "background": {
    "scripts": ["background.js"]
  },
  "content_scripts": [{
    "matches": ["*://*.example.com/*"],
    "js": ["content.js"]
  }]
}

background.js

browser.runtime.onMessage.addListener((message, _, sendResponse) => {
  console.log("onMessage", message);
  if (message === "ping") {
    sendResponse("pong");
  }
});

content.js

var button = document.createElement("button");
button.innerText = "Send message";
button.addEventListener("click", sendMessage);
document.body.append(button);

function sendMessage(){
  console.log("Sending ping");
  browser.runtime.sendMessage("ping", (response) => {
    console.log(`Recieved ${response}`);
  });
}

I tested by visiting example.com and allowing the event page to stop, then clicking the "Send message " button. In the console I see that I send and “ping” and receive a “pong” response as expected. I tested on macOS 13.5.2 (22G91) and Firefox Nightly 119.0a1 (2023-09-10).

Could you share a demo extension and testing steps to reproduce the issue you’re seeing?

Simeon - @dotproto
Firefox Add-ons DevRel

1 Like

I have encountered essentially the same issue, with a couple of differences detailed below. It is rare, and I can find no way to trigger it. It just happens on a rare occasion.

So far it has happened exactly twice: Once while I was working in Firefox Dev. Ed. 115.0b9 on macOS 10.14.6, and once in Firefox Dev. Ed. 120.0b8 on macOS 13.4.1.

Like OP,

  • BGS uses browser.runtime.onMessage.addListener()
  • CS uses browser.runtime.sendMessage()
  • On first attempt, get error message, Error: Could not establish connection. Receiving end does not exist.
    On second attempt, get successful connection.

Unlike OP,

  • I am not using Manifest v3, but v2 with the persistent flag explicitly set to false.
  • It has only happened exactly twice in several weeks of development work.