关于 fix-errors
fix-errors 是指导开发者处理遥测仪表板中未处理错误的技能。它强调沿调用栈追溯无效数据的来源,并在来源不明时补充诊断信息,而非掩盖错误。
主要功能
- 01从调用栈追踪数据流
- 02定位无效数据的产生端
- 03来源不明时补充错误上下文
- 04阅读错误构造代码再制定修复
- 05运行相关测试并检查编译
适用场景
- 01排查遥测仪表板中的未处理错误
- 02诊断跨进程传递的格式错误数据
- 03区分错误根因与崩溃位置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ vscode --skill fix-errors安装后在 Claude Code 中输入/fix-errors确认已加载。
详细介绍
修复来自遥测仪表板的未处理错误时,问题通常会包含错误消息、堆栈跟踪、命中次数和受影响的用户数。
方法
1. 不要在崩溃位置修复
错误会在堆栈跟踪中的特定行显现,但修复几乎从来不应该放在那里。在崩溃位置修复(例如,在 revive() 函数中添加 typeof 检查、用 try/catch 吞掉错误,或返回回退值)只会掩盖真正的问题。无效数据仍会流经系统,并在其他地方导致故障。
2. 沿调用堆栈向上追踪数据流
从下到上阅读堆栈跟踪中的每一帧。对于每一帧,弄清楚:
- 正在传递什么数据,以及预期是什么
- 数据来源(IPC 消息、扩展 API 调用、存储、用户输入等)
- 数据在该处是否可能已损坏或格式错误
目标是找到无效数据的生产方,而不是因其崩溃的消费方。
3. 如果无法仅凭堆栈确定生产方
有时堆栈跟踪只显示接收/消费方(例如,IPC 服务端处理程序)。发送方位于另一个进程中,不会出现在堆栈里。这种情况下:
- 在消费方位置丰富错误消息,添加诊断上下文:无效数据的类型、其值的截断表示,以及接收到该数据的操作/命令。这些信息会通过未处理错误管道自动流入错误遥测仪表板。
- 不要静默吞掉错误——让错误继续抛出,以便其仍显示在遥测中,同时提供足够的上下文,以便在下一轮遥测中识别发送方。
- 考虑在抛出错误的底层验证函数中也添加相同的补充信息(例如,在错误消息中包含无效值),这样无论调用位置如何,遥测都能捕获这些信息。
4. 如果可以确定生产方
直接修复生产方:
- 在通过 IPC 发送、存储或传递给 API 之前验证或清理数据
- 确保序列化/反序列化能正确保留类型(例如,URI 对象应序列化为
UriComponents对象,而不是字符串)
示例
给定如下堆栈跟踪:
at _validateUri (uri.ts) ← validation throws
at new Uri (uri.ts) ← constructor
at URI.revive (uri.ts) ← revive assumes valid UriComponents
at SomeChannel.call (ipc.ts) ← IPC handler receives arg from another process
错误的修复方式:在 URI.revive 中添加 typeof 检查,对非对象输入返回 undefined。这会让错误不再显现,但调用方仍然需要有效的 URI,之后还是会失败。
正确的修复方式(生产方未知时):在 IPC 处理程序层和 _validateUri 函数中丰富错误信息,加入实际的无效值,让遥测揭示发送的数据及其来源。示例:
// In the IPC handler — validate before revive
function reviveUri(data: UriComponents | URI | undefined | null, context: string): URI {
if (data && typeof data !== 'object') {
throw new Error(`[Channel] Invalid URI data for '${context}': type=${typeof data}, value=${String(data).substring(0, 100)}`);
}
// ...
}
// In _validateUri — include the scheme value
throw new Error(`[UriError]: Scheme contains illegal characters. scheme:"${ret.scheme.substring(0, 50)}" (len:${ret.scheme.length})`);
正确的修复方式(生产方已知时):修复发送格式错误数据的代码。例如,如果某个身份验证提供程序在调用日志记录器创建函数时传入字符串化的 URI,而不是 UriComponents 对象,就应修改该调用位置,传入正确的对象。
修复前先了解错误的构造方式
提出任何修复方案之前,务必先找到并阅读构造该错误的代码。在代码库中搜索错误类名或错误消息中的独特子字符串。构造错误的代码会揭示:
- 触发错误的条件——阈值、验证检查、状态断言
- 错误所编码的分类或类别——错误可能有不同子类型,需要采用不同的修复策略
- 错误参数的含义——消息中包含的数值、比率或标志通常编码了诊断上下文
- 错误是否可处理——有些错误是基于阈值的警告,而阈值按设计可能会被合理地超出
利用这些信息确定正确的修复策略。构造错误的代码才是事实依据——不要仅凭错误消息就假定其含义。
示例:监听器泄漏错误
搜索 ListenerLeakError 会找到 src/vs/base/common/event.ts,其中的构造代码揭示了:
const kind = topCount / listenerCount > 0.3 ? 'dominated' : 'popular';
const error = new ListenerLeakError(kind, message, topStack);
阅读这段代码可以得知:
- 根据比例,错误分为两类
- 占主导(比例 > 30%):一条代码路径产生了大多数监听器 → 问题出在这条代码路径上,应修复其释放逻辑
- 普遍(比例 ≤ 30%):许多不同的代码路径各自产生少量监听器 → 找到的堆栈跟踪并非根本原因;它只是众多相似堆栈中最常见的一个。应改为调查事件发射器及其订阅者的总数
- 对于普遍型泄漏:不要移除出现在顶部堆栈中的缓存、池化或复用模式——这些模式旨在解决其他问题。如果总数是设计如此(例如,许多菜单都订阅了共享的上下文键服务),请将问题标记为“暂不计划”
这一分析来自对构造代码的阅读,而非对监听器泄漏规则的死记硬背。
指南
- 优先补充错误消息,而不是添加 try/catch 防护
- 对错误消息中包含的任何用户可控值进行截断(以避免泄露个人身份信息,并限制消息长度)
- 不要更改共享工具函数(如
URI.revive)的行为,以免影响所有调用方;应在具体调用点或生产者处修复 - 做出更改后,运行相关的单元测试
- 在宣布工作完成前,通过构建任务检查是否存在编译错误