How to Fix: Could not establish connection. Receiving end does not exist

If you are developing a Chrome extension with message passing, then you might have received the following error: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.. There was some type of behavior change that happened post Chrome 72 that affected the way channels are opened. Essentially, if you try to call runetime.connect() before you have a a channel open, then you will get this error (Stackoverflow).

So here’s how I setup Message Passing in my Chrome extension to avoid this error.

Manifest.json

I originally had my content script defined in my manifest and used the MATCHES property to have it load on all URLs. However, I learned that if you try to submit a Chrome extension to the webstore that uses a content script that can run on any URL, it will be subject to a longer review time and higher scrutiny and might get rejected. So the simple solution was just to inject my script manually through message passing.

{
    "name": "Simple On Page SEO",
    "version": "1.2",
    "description": "SEO on page analyzer. Look for keyword density, keyword checker in title and meta tags.",
	"manifest_version": 2,
	"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
      "browser_action": {
				"default_popup": "popup.html",
				"default_icon": "icon.png"
		 },
		 "icons": {
		 "128": "icon.png" },
       "permissions": [
				"activeTab",
				"storage"
       ]
    
}

Recall that the popup script is the script that is attached to the popup window on your Chrome extension. I first injected my content script to the page once my extension was loaded.

window.addEventListener('load', (event) => {
  chrome.tabs.executeScript(null, {
    file: 'content.bundle.js', //my content script
  }, () => {
      connect() //this is where I call my function to establish a connection
    });
  });
});

That injects the script then calls my connect function to establish a connection.

function connect() {
  chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
    const port = chrome.tabs.connect(tabs[0].id);
    port.postMessage({ function: 'html' });
    port.onMessage.addListener((response) => {
      html = response.html;
      title = response.title;
      description = response.description;
    });
  });
}

This creates a connection for message passing and immediately posts a message to that new connection.

Content Script

Remember that the content script runs on the actual webpage that you are on. In this script I listen for a connection and respond to messages:

chrome.runtime.onConnect.addListener((port) => {
  port.onMessage.addListener((msg) => {
    if (msg.function == 'html') {
      port.postMessage({ html: document.documentElement.outerHTML, description: document.querySelector("meta[name=\'description\']").getAttribute('content'), title: document.title });
    }
  });
});

Setting up my Chrome extension like this allowed me to do message passing without receiving the error: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.


See also