Build Chrome extensions quicker with a TypeScript boilerplate project

Solved: The message port closed before a response was received

ExtensionNinja | 6/9/2022

I encountered the “port closed” exception after converting my extension to Manifest v3. This exception should be mostly harmless, but it was affecting the logic of my extension. I needed to find the root cause.

After some digging I found in Chrome extension documentation that browser.runtime.onMessage should call sendResponse if browser.runtime.sendMessage defines a callback listening for a message. I was pretty sure I was doing that already. Then noticed that during Manifest v3 conversion in some places I started using “await browser.runtime.sendMessage()” instead of passing a callback. This changed runtime behavior. Awaiting for a Promise does not throw an exception if sendResponse is not called in browser.runtime.onMessage.

I am still puzzled why I started noticing this after moving to promises even though using promises does not throw an exception. I probably made other refactorings for the Manifest v3 transition that changed code path for the browser.runtime.onMessage handler.

Lesson learned. Once you commit to the async/await pattern you should use it consistently across your code base.

Code example:

function start() {
    chrome.runtime.sendMessage("hello", (result) => {

async function startAsync() {
    const result = await chrome.runtime.sendMessage("hello");

// This will make sendMessage in start() throw an exception. startAsync() will be fine and sendMessage will return a Promise with undefined value.
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    // The fix is to call sendResponse() before this function returns
    // or return true and then call sendResponse() async.

chrome.action.onClicked.addListener(tab => {
        target: { tabId: },
        function: start, // Change to startAsync to observe different behaviour

Don't miss latest Chrome extension developer news

Join Newsletter