Communication between popup.js and content-script.js throws 'The message port closed before a response was received' in chrome extension

Viewed 47

popup.html

<button id="go">Go</button>
<script src="assets/js/popup.js"></script>

popup.js

document.getElementById('go').addEventListener('click', async () => {
  let [tab] = await chrome.tabs.query({active: true, currentWindow: true});

  chrome.tabs.sendMessage(tab.id, {go: true}, (response) => {
    if (!chrome.runtime.lastError) {
      console.log('fine');
    } else {
      // This will print the mentioned error in the console
      console.log(chrome.runtime.lastError);
    }
  });
});

content-script.js

chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  // It returns the object correctly // {go: true}
  console.log(request);
  if(request.go) {
    // This causes the error.
    // When I remove this line, `fine` will be printed in the console from the `if` in the `popup.js` file above.
    const go = await chrome.storage.local.get('something');
    console.log(go);
  }
});

And I have nothing in my background.js.

The issue is that it returns the error: The message port closed before a response was received when clicking on the start button.

I have added return true and sendResponse({status: true}) either, but didn't work!

Why do these happen and how to fix them?

1 Answers

Check if the content-script is being injected correctly, as shown by the chrome extension docs. If you're injecting statically, make sure your manifest.json looks like this

"content_scripts": [
   {
     "matches": ["https://*.nytimes.com/*"],
     "css": ["my-styles.css"],
     "js": ["content-script.js"]
   }
 ],

if that does not work, then the problem might be with the async function you're calling in the content-script. To use it asynchronously, a return true can be added in the listener.

Doing so, you can remove the async from the function and use

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    console.log(`request: ${request.go}`);
    if(request.go) {
      chrome.storage.local.get('something')
        .then(go => console.log(go))
    }
    return true;
});

With this code, the problem The message port closed before a response was received is solved, but another one pops up -- A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received. This error happens because no response was sent in channel, and you have to add a sendResponse to solve it, like:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    console.log(`request: ${request.go}`);
    if(request.go) {
      chrome.storage.local.get('something')
        .then(go => console.log(go))
    }
    sendResponse('hello from content-script')
    return true;
});

Another solution with the async code: you could wrap the code in a async function, as in:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    console.log(`request: ${request.go}`);
    if(request.go) {
      getSomething();
    }
    sendResponse('hello from content-script');
    return true;
});

async function getSomething() {
  const go = await chrome.storage.local.get('something');
  console.log(go);
}
Related