You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome Extension v3迁移遇runtime.lastError连接错误,求技术协助

问题描述

我正在开发一款基于OpenAI API的Chrome扩展,功能是对网页选中的文本生成摘要或提取要点。目前正将其从manifest v2迁移至manifest v3,运行代码时出现错误:Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist。我是Chrome扩展开发新手,附上相关代码,恳请提供解决建议与技术帮助。


manifest.json 文件

{
  "manifest_version": 3,
  "name": "Text Summarizer and Major Points generator",
  "version": "1.0",
  "description": "A Chrome extension that uses OpenAI API to summarize or produce major poinnts on the selected text.",
  "permissions": [
    "contextMenus",
    "storage",
    "activeTab",
    "scripting",
    "tabs"
  ],
  "action": {
    "default_popup": "popup.html"
  },
  "icons": {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  },
  "host_permissions": [
    "https://*.openai.com/"
  ],
  "background": {
    "service_worker": "background.js"
  },
 
  "options_page": "options.html"
 
}

background.js 文件

// background.js
// Generate a menu that gives an option for summary generation and major point generation
chrome.runtime.onInstalled.addListener(function () {
  chrome.contextMenus.create({
    id: 'summarize',
    title: 'Summarize it',
    contexts: ['selection'],
  });

  chrome.contextMenus.create({
    id: 'majorpoints',
    title: 'Major Point it',
    contexts: ['selection'],
  });
});

chrome.contextMenus.onClicked.addListener(async function (info, tab) {
  if (info.menuItemId === 'summarize' || info.menuItemId === 'majorpoints') {
    // Inject the content script
    chrome.scripting.executeScript( { 
      target:{tabId: tab.id},
      function: fetchDataAndInteract,
      args: [info], 
    });
    }
  });
  

      async function fetchDataAndInteract (info) {

      chrome.storage.sync.get('openaiApiKey', async function (apiKeyData) {
        const apiKey = apiKeyData.openaiApiKey;

        if (!apiKey) {
          alert('Please set your OpenAI API key in the extension settings.');
          return;
        }
   
        const action = info.menuItemId;
        const selectedText = info.selectionText;
        const prompt = action === 'summarize' ? `Summarize: ${selectedText}` : `Write the major point of this post: ${selectedText}`;

        const response = await fetch('https://api.openai.com/v1/completions', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${apiKey}`,
          },
          body: JSON.stringify({
            prompt: prompt,
            max_tokens: 100,
            n: 1,
            stop: null,
            temperature: 0.3,
          }),
        });

        const resultData = await response.json();

        if (resultData.choices && resultData.choices.length > 0) {
          const resultText = resultData.choices[0].text.trim();
          const resultTitle = action === 'summarize' ? 'Summary:' : 'Major Points:';

          chrome.scripting.executeScript({
            target: { tabId: tab.id },
            function: showResult,
            args: [resultTitle, resultText],
          });

        } else {
          console.error('No choices returned from API call.');
        }
      });
    }

    async function showResult(resultTitle, resultText) {
      const popup = document.createElement('div');
      popup.style.position = 'fixed';
      popup.style.top = '20%';
      popup.style.left = '50%';
      popup.style.transform = 'translate(-50%, -50%)';
      popup.style.zIndex = '9999';
      popup.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
      popup.style.padding = '20px';
      popup.style.borderRadius = '10px';
      popup.style.maxWidth = '80%';
      popup.style.textAlign = 'center';
      popup.style.fontFamily = 'Arial, sans-serif';
    
      const title = document.createElement('h3');
      title.innerText = resultTitle;
      title.style.marginBottom = '10px';
      popup.appendChild(title);
    
      const result = document.createElement('p');
      result.innerText = resultText;
      popup.appendChild(result);
    
      document.body.appendChild(popup);
    
      setTimeout(function () {
        document.body.removeChild(popup);
      }, 10000);
    }

content.js 文件

//content.js
chrome.runtime.onMessage.addListener( function (request, sender, sendResponse) {
  if (request.action === 'showResult') {
    console.log('Message received in content.js:', request);
    const { resultTitle, resultText } = request;
    const popup = document.createElement('div');
  popup.style.position = 'fixed';
  popup.style.top = '20%';
  popup.style.left = '50%';
  popup.style.transform = 'translate(-50%, -50%)';
  popup.style.zIndex = '9999';
  popup.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
  popup.style.padding = '20px';
  popup.style.borderRadius = '10px';
  popup.style.maxWidth = '80%';
  popup.style.textAlign = 'center';
  popup.style.fontFamily = 'Arial, sans-serif';

  const title = document.createElement('h3');
  title.innerText = resultTitle;
  title.style.marginBottom = '10px';
  popup.appendChild(title);

  const result = document.createElement('p');
  result.innerText = resultText;
  popup.appendChild(result);

  document.body.appendChild(popup);

  setTimeout(function () {
    document.body.removeChild(popup);
  }, 10000);

    sendResponse({ success: true });
  } 
  // sendResponse({ success: true });
});
// popup.js
document.addEventListener('DOMContentLoaded', async function () {
  chrome.runtime.sendMessage({ action: 'processText' }, async function (globalData) {
    const title = document.getElementById('title');
    const result = document.getElementById('result');

    if (globalData && (globalData.action === 'summarize' || globalData.action === 'majorpoints')) {
      const apiKey = globalData.apiKey;
      const selectedText = globalData.selectedText;
      const prompt = globalData.action === 'summarize' ? `Summarize: ${selectedText}` : `Major Points of: ${selectedText}`;

      title.textContent = globalData.action === 'summarize' ? 'Summary:' : 'Major Points:';
      result.textContent = 'Loading...';

      try {
        const response = await fetch('https://api.openai.com/v1/completions', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${apiKey}`,
          },
          body: JSON.stringify({
            prompt: prompt,
            max_tokens: 50,
            n: 1,
            stop: null,
            temperature: 0.7,
          }),
        });

        const data = await response.json();
        const resultText = data.choices[0].text.trim();
        result.textContent = resultText;
      } catch (error) {
        console.error('Error fetching the summary/Major Point:', error);
        result.textContent = 'An error occurred. Please try again later.';
      }
    }
  });
});
<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        width: 300px;
        height: 200px;
        font-family: Arial, sans-serif;
      }
    </style>
  </head>
  <body>
    <h3 id="title"></h3>
    <div id="result"></div>
    <script src="popup.js"></script>
  </body>
</html>

解决建议

1. 错误根源

Could not establish connection. Receiving end does not exist 错误来自popup.js中的chrome.runtime.sendMessage调用——后台脚本(background.js)没有监听processText这个消息类型,导致发送方找不到接收端,触发连接错误。同时代码还存在变量作用域、逻辑冗余等问题。

2. 具体修复步骤

(1)移除popup.js无效的消息发送逻辑

当前扩展通过右键菜单触发功能,popup无需自动请求处理,直接简化popup.js:

// popup.js 简化后
document.addEventListener('DOMContentLoaded', function () {
  const title = document.getElementById('title');
  title.textContent = '选中文本后右键使用功能';
});

(2)修复background.js变量作用域问题

fetchDataAndInteract函数中无法直接访问tab.id,需要把它作为参数传入:

chrome.contextMenus.onClicked.addListener(async function (info, tab) {
  if (info.menuItemId === 'summarize' || info.menuItemId === 'majorpoints') {
    chrome.scripting.executeScript({ 
      target: {tabId: tab.id},
      function: fetchDataAndInteract,
      args: [info, tab.id], // 传入tab.id
    });
  }
});

async function fetchDataAndInteract (info, tabId) {
  // ... 原有代码
  if (resultData.choices && resultData.choices.length > 0) {
    // ... 原有代码
    chrome.scripting.executeScript({
      target: { tabId: tabId }, // 使用传入的tabId
      function: showResult,
      args: [resultTitle, resultText],
    });
  }
  // ... 原有代码
}

(3)修复API请求中的转义字符

background.js里的&amp;&amp;和&gt;是HTML转义字符,要改成JS原生的&&和>:

// 原代码
if (resultData.choices &amp;&amp; resultData.choices.length &gt; 0) {
// 修改后
if (resultData.choices && resultData.choices.length > 0) {

(4)统一UI展示逻辑

你同时在background.js和content.js中实现了相同的弹窗逻辑,二选一即可:

  • 保留background.js的showResult注入逻辑,直接删除content.js文件;
  • 若想用content.js展示弹窗,需确保调用chrome.runtime.sendMessage前已注入content.js。

(5)优化权限配置

把host_permissions改成更精确的OpenAI API地址:

"host_permissions": [
  "https://api.openai.com/v1/*"
]

3. 测试验证

  1. 重新打包扩展并加载到Chrome;
  2. 选中网页文本,右键点击对应菜单;
  3. 检查是否弹出结果弹窗,同时查看控制台(F12)是否还有错误。

内容的提问来源于stack exchange,提问作者Reenu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 20:22:04