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})`);
適切な修正(生成元が判明している場合):不正なデータを送信するコードを修正します。たとえば、認証プロバイダーがロガー作成呼び出しに UriComponents オブジェクトではなく、文字列化された URI を渡している場合は、その呼び出し箇所を修正し、適切なオブジェクトを渡すようにします。
修正前にエラーの構築箇所を確認する
修正を提案する前に、必ずエラーを構築するコードを見つけて読みます。コードベースでエラークラス名、またはエラーメッセージの特徴的な一部を検索します。構築コードから、以下のことが分かります。
- どのような条件でエラーが発生するか — しきい値、検証チェック、状態アサーション
- エラーがどのような分類やカテゴリを表しているか — エラーに異なる修正方針を必要とするサブタイプがある場合があります
- エラーのパラメーターが何を意味するか — メッセージに含まれる数値、比率、フラグが診断情報を示していることがよくあります
- エラーが対処可能かどうか — しきい値に基づく警告の中には、設計上、しきい値を超えても問題ないものがあります
この理解に基づいて、適切な修正方針を判断します。構築コードが信頼できる情報源です。メッセージだけを見てエラーの意味を決めつけないでください。
例:リスナーリークエラー
ListenerLeakError を検索すると src/vs/base/common/event.ts にたどり着きます。そこで構築コードを確認すると、次のことがわかります。
const kind = topCount / listenerCount > 0.3 ? 'dominated' : 'popular';
const error = new ListenerLeakError(kind, message, topStack);
このコードを読むと、次のことがわかります。
- エラーは比率に基づいて2つのカテゴリに分類されます
- Dominated(比率 > 30%):1つのコードパスがリスナーの大半を占めています。そのコードパスが問題なので、破棄処理を修正してください。
- Popular(比率 ≤ 30%):さまざまなコードパスがそれぞれ少数のリスナーを追加しています。特定されたスタックトレースは根本原因ではありません。多数の中で最も同一性が高いスタックにすぎません。代わりに、エミッターとそのサブスクライバー全体を調査してください。
- Popular なリークの場合:上位のスタックに現れるキャッシュ/プーリング/再利用パターンは削除しないでください。これらは別の問題を解決するために存在しています。合計数が設計どおりの場合(たとえば、多数のメニューが共有コンテキストキーサービスをサブスクライブしている場合)は、課題を「対応予定なし」としてクローズしてください。
この分析は、リスナーリークに関するルールを暗記したのではなく、構築コードを読んで導き出したものです。
ガイドライン
- try/catch ガードを追加するより、エラーメッセージの情報を充実させてください。
- エラーメッセージにユーザーが制御できる値を含める場合は、個人情報の漏えいを防ぎ、メッセージの長さを抑えるために切り詰めてください。
- 共有ユーティリティ関数(
URI.reviveなど)の動作を、すべての呼び出し元に影響する形で変更しないでください。特定の呼び出し箇所または生成元で修正してください。 - 変更後は、関連する単体テストを実行してください。
- 作業完了を宣言する前に、ビルドタスクでコンパイルエラーがないことを確認してください。