background script: Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist

I am trying to create an offscreen document that plays an audio for my chrome extension. My background.js script receives a message that triggers the initiation of the offscreen document, which contains code to run an audio file uploaded by the user. Every time I run it, I get this in the service worker console:

Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.

And upon reloading the page, I get:

Error creating offscreen document: Error: Only a single offscreen document may be created.

Here is my code:

manifest.json:

{
    "manifest_version": 3,
    "name": "Custom",
    "description": "",
    "version": "1.0",
    "action": {
        "default_popup": "templates/popup.html",
        "default_icon": "images/logo.png"
    },
    "background": {
        "service_worker": "scripts/background.js"
    },
    "icons": {
        "16": "images/logo.png",
        "32": "images/logo.png",
        "48": "images/logo.png",
        "128": "images/logo.png"
    },
    "permissions": [
        "storage",
        "activeTab",
        "scripting",
        "offscreen"
    ],
    "host_permissions": [
        "https://www.netflix.com/watch/*"
    ],
    "content_scripts": [
        {
            "matches": [
                "https://www.netflix.com/watch/*"
            ],
            "js": [
                "scripts/content.js"
            ]
        }
    ]
}

content.js:

// Try to find the video element immediately
if (!checkForVideoElement()) {
    // If not found, use a mutation observer to watch for changes
    const observer = new MutationObserver((mutationsList, observer) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList') {
                if (checkForVideoElement()) {
                    // Stop observing once the video element is found
                    observer.disconnect();
                    break;
                }
            }
        }
    });

    // Start observing the document body for added nodes
    observer.observe(document.body, { childList: true, subtree: true });
}

function checkForVideoElement() {
    var video = document.querySelector("video");
    if (video) {
        console.log(video);

        if (video.currentTime <= 60) {
            console.log("Video found less than 60s");
            // asking background.js to create an offscreen document and play the sound
            chrome.runtime.sendMessage("runOffscreenTask");
        }

        return true;
    }
    return false;
}

background.js:

// Fix for crbug.com/1185241
if (typeof chrome.runtime.onMessage !== "undefined") {
    const { onMessage } = chrome.runtime;
    const { addListener } = onMessage;
    onMessage.addListener = fn => addListener.call(onMessage, (msg, sender, respond) => {
        const res = fn(msg, sender, respond);
        if (res instanceof Promise) return !!res.then(respond, console.error);
        if (res !== undefined) respond(res);
    });
}

// Re-inject content scripts on extension install/update
chrome.runtime.onInstalled.addListener(async () => {
    const contentScripts = chrome.runtime.getManifest().content_scripts;
    for (const cs of contentScripts) {
        const tabs = await chrome.tabs.query({ url: cs.matches });
        for (const tab of tabs) {
            chrome.scripting.executeScript({
                target: { tabId: tab.id, allFrames: cs.all_frames },
                files: cs.js,
                injectImmediately: cs.run_at === 'document_start',
            });
        }
    }
});

// Tab update listener
chrome.tabs.onUpdated.addListener(function (tabId, info, tab) {
    if (info.status === "complete" && tab.url.indexOf("netflix.com/watch/") !== -1) {
        chrome.tabs.update(tabId, { muted: true });

        chrome.scripting.executeScript({
            target: { tabId: tabId },
            files: ['scripts/content.js']
        }, () => {
            console.log("Content script injected successfully");
        });

        chrome.storage.local.get("soundDuration", function (result) {
            let duration = result.soundDuration || 0;
            console.log("Duration of the sound: " + duration + "ms");
            setTimeout(() => {
                chrome.tabs.update(tabId, { muted: false });
            }, (8000 - duration));
        });
    }
});

// Message listener for offscreen task
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message === "runOffscreenTask") {
        offScreenTask();
        return true;
    }
});

async function offScreenTask() {
    await setupOffscreenDocument('offscreen.html');
    chrome.runtime.sendMessage("playSound");
    return true;
}

let creating; // A global promise to avoid concurrency issues
async function setupOffscreenDocument() {
    const offscreenUrl = chrome.runtime.getURL("offscreen.html");
    const existingContexts = await chrome.runtime.getContexts({
        contextTypes: ['OFFSCREEN_DOCUMENT'],
        documentUrls: [offscreenUrl]
    });

    if (existingContexts.length > 0) {
        return;
    }

    if (creating) {
        await creating;
    } else {
        creating = chrome.offscreen.createDocument({
            url: offscreenUrl,
            reasons: ["AUDIO_PLAYBACK"],
            justification: "Playing Custom Sound",
        });
        await creating;
        creating = null;
    }
}

offscreen.js:

console.log("Offscreen script loaded");

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    console.log("Offscreen script received message:", message);

if ('crbug.com/1185241') { // replace with a check for Chrome version that fixes the bug
    const { onMessage } = chrome.runtime, { addListener } = onMessage;
    onMessage.addListener = fn => addListener.call(onMessage, (msg, sender, respond) => {
        const res = fn(msg, sender, respond);
        if (res instanceof Promise) return !!res.then(respond, console.error);
        if (res !== undefined) respond(res);
    });
}

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    console.log("Offscreen script received message:", message);

    if (message.action === "playSound") {
        playSound();
    }
});

function playSound() {
    chrome.storage.local.get("Sound", function (result) {
        let audio = result.Sound;
        if (audio) {
            console.log("Audio found");
            // Convert the data URI to a Blob
            fetch(audio)
                .then(response => response.blob())
                .then(blob => {
                    const audioUrl = URL.createObjectURL(blob);
                    const audioElement = new Audio(audioUrl);
                    audioElement.play().then(() => {
                        console.log("Audio played successfully");
                    }).catch(error => {
                        console.error("Error playing audio:", error);
                    });
                })
                .catch(error => {
                    console.error("Error converting audio data:", error);
                });
        } else {
            console.log("Audio not found");
        }
    });
}

I am not sure how to fix the “receiving end” function, and I am confused as to why the “single offscreen” function is occurring even though I believe I have correctly implemented a setupOffscreenDocument() function in my background.js too.

I have already looked at this answer on re-injecting the content script and this answer on separating async requests from message listeners and tried my best to implement all the suggestions, but my code still doesn’t work. Even reloading the page manually doesn’t do anything – just shows the “single offscreen document” error.

I am very new at this, so I may have gone very obviously wrong in quite a few places!