花一下午做了个 Chrome 插件:任何页面上的 console 报错自动捕获,一键把报错、完整堆栈、页面上下文打包成一份能直接丢给 AI 的提问;配个 API,还能让 AI 在页面右下角直接给出诊断。先看图,这是它在我自己开发页上跑的样子,两条 warn 是页面埋点脚本自己打的,打出来那一刻就被逮住了:

它具体能干嘛
功能摊开说:
- 捕获四种信号:
console.error、console.warn、未捕获异常、Promise rejection。页面一打印就进列表,不用开 devtools 盯着。 - 每条报错三个按钮:「生成 AI 提问」把错误信息 + 完整堆栈 + 页面地址 + 报错类型组装成结构化提问,复制到剪贴板;「堆栈」点击展开全文;「AI 直连诊断」打你配的 AI 接口,结论直接出在面板里。
- 整个 UI 套在 Shadow DOM 里,挂在任何页面右下角,不吃页面样式。
重点是第一个。它生成的提问长这样(上面图里那条 warn,点一下复制出来的,节选):
ruby
我在前端遇到一个报错,请帮我定位原因并给出修复方案。
## 错误信息
parseImageXUrl "tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Iy25bqV5LiW55WM5LmL5LiL:q75.awebp" parse error
## 调用堆栈
at console.overrideMethod (chrome-extension://fmkadmapgofadopljbjfkapdkoienihi/build/installHook.js:1:174822)
at console.<anonymous> (https://lf-web-assets.juejin.cn/obj/juejin-web/xitu_juejin_web/9bb69b6.js:4:2273597)
at console.m (https://lf-web-assets.juejin.cn/obj/juejin-web/xitu_juejin_web/9bb69b6.js:4:2252589)
at push.Ct._hook.window.console.<computed> [as error] (https://lf-web-assets.juejin.cn/obj/juejin-web/xitu_juejin_web/9bb69b6.js:4:1722126)
## 页面上下文
- URL: https://juejin.cn/
- 类型: ERROR
- 触发次数: 11 次(已自动合并重复报错)
要求:先解释直接原因,再按可能性从高到低列出 3 个根因,最后给最小修复代码。
拿这个去问,和只粘一句「document.cookie 警告」去问,回答质量是两回事。
AI 直连诊断:连复制这步都省了
「生成 AI 提问」还要手动粘贴一次。直连诊断把这步也省了:面板里点「API 设置」,填一个 OpenAI 兼容接口(base、key、model 三个框),然后对任意一条报错点「AI 直连诊断」,结论直接出在报错条目下方。
请求不走页面。content script 里直接 fetch 会被页面的 CORS 拦住,所以请求放在插件的 background service worker 里转发,页面完全无感。
实测效果,挑了一条真实报错点下去:

结论几秒就出来:直接原因是 parseImageXUrl 拿到了非法的 URL 字符串------tplv-xxx.awebp 这种字节跳动系私有资源标识符,不是真 URL;根因第一条直接点到业务代码把标识符当 URL 传进了解析函数,连抖音、火山图床这种具体来源都点了出来。这个具体程度,只粘一句报错是问不出来的------完整堆栈和页面上下文都在提问里。
这版还有两个首测才暴露的小问题,顺手修了:诊断结果原本是原始文本,** 和 ## 全露着,给它加了个轻量 Markdown 渲染;设置表单保存后不收起,占着面板,改成保存即收起。
四个真实踩的坑
代码量不大,400 行,打字没花多少时间,时间全花在坑上。这四个坑没有一个是被提前预警的,全是跑起来才暴露。
坑一:插件默认看不见页面的 console。
第一版注入完跑起来,一条都捕获不到。原因是 Chrome 把插件脚本和页面脚本关在两个房间里,页面自己调的 console.error,插件脚本在另一个房间里听不见。
解法是在 manifest 里声明这个脚本跑进页面自己的房间(MV3 里叫 world: "MAIN"):
json
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["injected.js"],
"run_at": "document_start",
"world": "MAIN"
}
]
进了页面的房间,又摸不到插件 API,捕获结果要用 postMessage 中转回去。这里还有个暗坑:postMessage 是广播,页面自己的脚本也能发同格式的消息,不校验来源字段,别人就能往你面板里塞假报错。
文章开头那张图就是修复后的效果:页面打印的那一刻就被逮住,不用开 devtools。
坑二:堆栈被截断了。
实测时捕获正常、复制正常,点开堆栈只能看到一部分。查下来是两个原因叠加:捕获时我把堆栈截断在 2000 字符,面板里又只给了 140px 高。对调试工具来说堆栈就是命脉,最关键的那一帧往往在第 2000 字符后面。
修复:截断上限提到 6000,堆栈区域点击展开全文。

坑三:同一条报错,列表里刷了 16 条。
拿一个常去的页面测试,面板里齐刷刷一排一模一样的报错。页面里一个函数持续出错,触发了 16 次,我就老老实实记了 16 条,列表被它占满,真正要看的新报错全被淹掉。

修复是加去重:级别和内容都相同的报错合并成一条,次数累加,时间和堆栈更新成最近一次,条目旁边显示 ×N。徽章上的数字改成总触发次数,列表只留唯一条目,跟 DevTools 的使用习惯对齐。

坑四:同一个页面切路由,报错一直堆。
跟坑三同一时间发现的:页面是 SPA,在不同 tab 之间来回切,旧视图的报错一直留在列表里,切几下列表全是上一屏的过期报错。
一开始想在 content script 里监听路由变化,发现行不通:插件脚本和页面 JS 是隔离的,页面框架(React Router / Vue Router)自己调的 history.pushState,插件这边拦不到。所以这段逻辑只能也放进跑在页面房间里的注入脚本:包住 history 的 pushState 和 replaceState,再监听 popstate 和 hashchange,URL 一变就广播一条消息,面板收到后清空列表。

还有两个小坑一句话带过:AI 直连在 content script 里直接 fetch 会被页面 CORS 拦住,得让 background service worker 转发;面板写了 display: flex 之后 hidden 属性会失效,要补一条强制规则。都不难,但都是跑起来才发现。
技术坑修完,第二轮实测又冒出五个体验问题:悬浮徽章不能拖、会挡内容;toast 靠右弹;报错标题一行就省略;堆栈和诊断会同时摊开;面板太窄,底部模板按钮换行。逐个改成:徽章可拖拽、位置记忆;标题留两行;堆栈和诊断互斥;面板加宽到 460px。这些没有一个是技术难点,但全是不真正用起来就发现不了的问题。修完这轮还有下一轮:面板展开会盖住徽章、设置没有取消按钮、长报错看不出能展开,还有 toast------放哪都被页面样式带偏,试了两次视口级定位都斗不过,最后认输收进面板里底部居中,不斗了。工具类 side project 的最后一公里,全是这种活。
速查表:常见报错怎么问 AI
提问模板是这插件里我最满意的部分,直接抄:
| 报错类型 | 提问模板 |
|---|---|
| TypeError: Cannot read properties of undefined | 「堆栈如下,列出数据链最可能断裂的 3 个位置,按可能性排序」 |
| Uncaught (in promise) | 「这个 rejection 没被捕获,定位调用链上缺失的 catch 或错误边界」 |
| Failed to fetch / CORS | 「帮我区分是后端没起、代理配置还是预检失败」 |
| React 重渲染警告 | 「找出触发循环更新的 state 和 effect 依赖」 |
| 水合/渲染不一致 | 「列出服务端和客户端渲染结果不一致的常见来源」 |
面板里还内置了一个万能追问:这类错误怎么在 ESLint、单测、CI 里提前拦截?把一次报错变成一次防线升级。
几句实话
400 行代码大部分不是我打的,我出决策,AI 出代码。但四个坑------看不见 console、堆栈截断、重复刷屏、路由累积------没有一个被提前预警,全是实测暴露的。
写代码越来越便宜,验证的成本一分钱没降。
下一版想做捕获开关和按报错类型过滤。你最近被什么报错折磨得最多?评论区贴一个,呼声最高的当下版素材。