mozilla / webextension-polyfill

A lightweight polyfill library for Promise-based WebExtension APIs in Chrome
Mozilla Public License 2.0
2.61k stars 209 forks source link

Chrome: "The message port closed before a response was received." #130

Closed lydell closed 6 years ago

lydell commented 6 years ago

Minimal repro repo: https://github.com/lydell/webextension-polyfill-messaging-issue

This happens with both v0.2.1 on npm and when built from commit 2537b23837d4752e3a6c3236c2776adbc89f1312.

Summary

If a browser.runtime.onMessage callback does not return a Promise, browser.runtime.sendMessage will be rejected with the following error, causing noise in the console:

"The message port closed before a response was received."

Workaround

Even if you don't want to send a response, always return a promise in your onMessage callback:

// background.js
browser.runtime.onMessage.addListener(message => {
  console.log("background: onMessage", message);

  // Add this line:
  return Promise.resolve("Dummy response to keep the console quiet");
});

You can also make the onMessage callback async to implicitly return a Promise (resolving to undefined in the below example).

// background.js
// Notice the `async` keyword.
browser.runtime.onMessage.addListener(async message => {
  console.log("background: onMessage", message);
});

Files

Copied over for convenience from: https://github.com/lydell/webextension-polyfill-messaging-issue

{
  "manifest_version": 2,
  "version": "0.0.0",
  "name": "Test",
  "background": {
    "scripts": [
      "browser-polyfill-npm.js",
      "background.js"
    ]
  },
  "content_scripts": [
    {
      "matches": [
        "<all_urls>"
      ],
      "js": [
        "browser-polyfill-master.js",
        "content.js"
      ]
    }
  ]
}
// background.js

browser.runtime.onMessage.addListener(onMessage);

function onMessage(message) {
  console.log("background: onMessage", message);

  // 1: Causes the following to be logged in content:
  // "The message port closed before a response was received."
  return undefined;

  // 2: Causes this response to be logged in content, as expected.
  // return Promise.resolve("response from background");

  // 3: Causes this error to be logged in content, as expected.
  // return Promise.reject(new Error("Could not respond"));

  // 4: Causes nothing at all to be logged in content!
  // I guess it is waiting for the deprecated `sendResponse` parameter to be
  // called.
  // return true;
}
// content.js

// 1: Unless background returns a Promise in its onMessage, this promise is
// rejected with:
// "The message port closed before a response was received."
browser.runtime
  .sendMessage("hello from content")
  .then(console.log, console.error);

// 2: This does not seem to cause any errors:
// chrome.runtime.sendMessage("hello from content");
// console.log("content: after chrome.runtime.sendMessage", chrome.runtime.lastError);

// 3: Inside the callback, `chrome.runtime.lastError` will be:
// "The message port closed before a response was received."
// It seems like if `sendMessage` defines a callback but the other end doesn't
// respond, Chrome is treating that as an error. Which makes sense.
// The question is how this should be handled in a Promise based API.
// chrome.runtime.sendMessage("hello from content", response => {
//   console.log("content: callback", response, chrome.runtime.lastError);
// });
// console.log(
//   "content: after chrome.runtime.sendMessage with callback",
//   chrome.runtime.lastError
// );

Solution?

Should the "The message port closed before a response was received." be detected, and the promise should be resolved with undefined?

Rob--W commented 6 years ago

Thanks for this report - I can confirm that this happens with and without --enable-features=NativeCrxBindings .

sabrinasong commented 5 years ago

What if the content script goes away after receiving the message, and cannot send a response?

Rob--W commented 5 years ago

What if the content script goes away after receiving the message, and cannot send a response?

Then the sendMessage promise is rejected with an object with a "message" property.

In Firefox, the message is "Message manager disconnected" In Chrome, the message is "The message port closed before a response was received."

OussamaBATOUCHE commented 5 years ago

hey , can some one give me simple solution

prathmesh4 commented 5 years ago

Unchecked runtime.lastError: The message port closed before a response was received. error

RobertJGabriel commented 5 years ago

Add a return true. It will force chrome to wait for the response.

chrome.extension.onMessage.addListener((request, sender, sendResponse) => {
  // Look up a term from the dictionary using the Ajax API.
  const lookupURL = `http://dictionary-lookup.org/${request.arg}`;
  sendRequest(lookupURL)
    .then(resp => {
      sendResponse(resp || '{}');
    })
    .catch(error => {
      sendResponse('{}');
    });
  return true; // Inform Chrome that we will make a delayed sendResponse
});

example

ghost commented 4 years ago

i think that the: return true; // Inform Chrome that we will make a delayed sendResponse

Doesn't solve the issue. Greetings.

reZach commented 4 years ago

@vrgomes is correct, this fix does not work - is there another resolution to this issue?

ghost commented 4 years ago

Any updates on this? Greetings.

reZach commented 4 years ago

Using @RobertJGabriel 's answer as a place to start, I found the error occurs when we neglect to send a response at all. I found I needed to call sendResponse in addition to returning true to prevent the error from happening. It doesn't matter that the object we send is empty. @vrgomes hope this helps

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
    // ..code
    sendResponse({});
    return true;
});
stanleykayzz commented 4 years ago

Hello, in my case I call sendResponse in my background.js then return true but like @vrgomes said are just delayed so the only difference is that I get error messages later in my background console.

ghost commented 4 years ago

Using @RobertJGabriel 's answer as a place to start, I found the error occurs when we neglect to send a response at all. I found I needed to call sendResponse in addition to returning true to prevent the error from happening. It doesn't matter that the object we send is empty. @vrgomes hope this helps

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
    // ..code
    sendResponse({});
    return true;
});

@reZach it looks like to work that way! But had to put on the content script on all calls this check: if(response == undefined || Object.keys(response).length == 0) return;

Greetings.

reZach commented 4 years ago

Yes, you will need that too @vrgomes. I forgot to put that in my reply.

Rob--W commented 4 years ago

The recent comments are about the non-polyfilledchrome messaging API.

The original reported issue has been fixed a long time ago (in version 0.3.0) by #140 . If you still encounter this issue, upgrade the polyfill to the latest version. If you can still reproduce this issue despite using the latest version, please file a new issue.

Deepansharora27 commented 3 years ago

I am facing this same issue when I am trying to develop a chrome extension. I have tried every possible method from making the function async , returning true at the end and returning a resolved promise object, but nothing seems to work for me . Can anyone help me with it ?

Here is my Code : `

chrome.runtime.onMessage.addListener(async (msg, sender, response) => {

if (msg.command == "fetchNotes") { //Processing the Request then after listening to the request firebase .database() .ref("/notes") .once("value") .then(function (snapshot) { response({ type: "result", status: "success", data: snapshot.val(), request: msg, }); });

}

// return true; // return Promise.resolve("Dummy response to keep the console quiet"); // return undefined; }); `

rpl commented 3 years ago

@Deepansharora27 in the code snippet you pasted the onMessage listener is registered chrome global which is the one natively provided by chrome and not the polyfilled one and so your issue isn't actually related to the webextension-polyfill (and chrome doesn't support returning a response by returning a promise, you have to return true to let chrome know that the listener is going to reply asynchronously and then call the response callback).

Deepansharora27 commented 3 years ago

@rpl HIi , I am returning true as well but it isn't working for me

rpl commented 3 years ago

@rpl HIi , I am returning true as well but it isn't working for me

I'm sorry but as I did mention in my previous comment yours isn't a polyfill issue, it is more likely an issue in how the extension is using the chrome APIs.

superhipz commented 2 years ago

I found why this error occur. DONT USE ASYNC AWAIT in your code. Just use promise and return true at last. I fixed it after trying many time

DrMalchev commented 2 years ago

This works for me: chrome.runtime.onMessage.addListener(async function ( message, sender, sendResponse ) { console.log(message.data); sendResponse({ data: "Hey!" }); return true; });

NaguDA commented 2 years ago

I am facing same issue, where I cannot disable chrome extensions as it is maintained by admin. Is there any way to get rid of this Issue.

Chrome: "The message port closed before a response was received."

Rob--W commented 2 years ago

I am facing same issue, where I cannot disable chrome extensions as it is maintained by admin. Is there any way to get rid of this Issue.

Chrome: "The message port closed before a response was received."

That has nothing to do with the issue tracked here.

Rob--W commented 2 years ago

FYI to those who are following here - Chrome may at some point support Promise as return values in the onMessage listener (without requiring return true) - see https://bugs.chromium.org/p/chromium/issues/detail?id=1185241

umrashrf commented 11 months ago

I found why this error occur. DONT USE ASYNC AWAIT in your code. Just use promise and return true at last. I fixed it after trying many time

I love async/await, it makes my code so much readable. it's a shame if I have to remove that to make it work.

navidshad commented 6 months ago

I found why this error occur. DONT USE ASYNC AWAIT in your code. Just use promise and return true at last. I fixed it after trying many time

This solution resolved my problem, try to do it and check if you did it right.

rejoanahmed commented 5 months ago

I found why this error occur. DONT USE ASYNC AWAIT in your code. Just use promise and return true at last. I fixed it after trying many time

worked like a charm. I had async keyword which caused the issue. after removing everything works

sfyang2 commented 4 months ago

I found why this error occur. DONT USE ASYNC AWAIT in your code. Just use promise and return true at last. I fixed it after trying many time

你的答案是对的,我想应该是加了async后返回的是promoise对象而不是true,导致事件没有等待.

test("测试promise", () => { const onMessage = function (callback: () => any) { const res = callback() if(res===true) //进入正确的逻辑 console.log('typeof', typeof res, isPromise: ${(res instanceof Promise)},); }

    onMessage(() => {
        return true
    })

  //这样就导致了错误了
    onMessage(async () => {
        return true
    })
})
Njoxpy commented 1 month ago

I was having the same issues. Someone told me that I should disable all the extensions I had installed. It worked, but the solution was not ideal!