error-debugging について
error-debugging は、クラッシュレポートやスタックトレース、ログから原因を追跡し、修正案と確認手順を示す開発者向けスキルです。必要に応じてトレースをシンボリケーションし、ソースコードと証拠に基づいて仮説を検証します。
主な機能
- 01クロスプラットフォームのエラー分析
- 02失敗フレームの特定
- 03難読化トレースのシンボリケーション
- 04根本原因の追跡と検証
- 05修正案の提示と確認実行
活用シーン
- 01クラッシュレポートやログの説明
- 02テスト、ビルド、実行時エラーの診断
- 03リリース版の難読化トレースの分析
- 04依頼された修正の適用と検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jablonkai/ skills --skill error-debuggingインストール後、Claude Code で/error-debuggingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/flutter-dart.mdkotlin-android-kmp.mdother-languages.mdrust-cpp.mdswift-apple.mdsymbolication.mdworked-examples.md詳細
error-debugging
目的
エラーメッセージを説明と修正方法に変換します。入力は、ユーザーが持っているものなら何でも構いません。スタックトレース、クラッシュレポート、パニック、ログの抜粋、ときにはスクリーンショットの説明だけの場合もあります。出力するのは、実際に失敗した行、その理由、変更すべき内容、そして変更が機能したことを確認する方法です。
価値の大部分は、その中間の手順にあります。例外を投げたフレームが、問題のあるフレームとは限りません。レンダリング処理での NullPointerException は通常、上流から、本来渡されるべきでない null が渡されたことを意味します。症状ではなく、原因を報告してください。
使用する場面
- ユーザーがスタックトレース、クラッシュレポート、パニック、ANR、エラーログを貼り付けた場合。質問の有無は問いません
- エラーの意味、アプリがクラッシュする理由、または障害の発生元についてユーザーが尋ねた場合
- リリースビルドのトレースを読み取るために、難読化解除またはシンボリケーションが必要な場合
- テストの失敗、ビルドの失敗、または実行時ログの根本原因を特定する必要がある場合
このスキルの対象外:
- 特定の障害がない状態でのプロジェクト全体のバグ調査 →
code-analyzer - まだ取得していない、失敗した GitHub Actions の実行 →
github-fix-ci-error(このスキルで実行を取得し、そのログに詳細なトレース分析が必要な場合は、こちらに戻ってください)
前提条件
分析の前に、次の 2 点を確認します。
1. どのプラットフォームで生成されたものですか? ユーザーの説明ではなく、トレースの形式から判断してください。説明が誤っていたり、なかったりすることはよくあります。
| トレース内のシグナル | プラットフォーム | リファレンス |
|---|---|---|
at com.foo.Bar.baz(Bar.kt:42)、Caused by:、FATAL EXCEPTION | Kotlin / Java / Android | kotlin-android-kmp.md |
ANR in、Input dispatching timed out、"main" prio=5 tid=1 のスレッドダンプ | Android ANR | kotlin-android-kmp.md |
kfun:、Uncaught Kotlin exception、kotlin.Throwable、ObjCException | Kotlin/Native(iOS 上の KMP) | kotlin-android-kmp.md |
androidx.compose.*、Snapshot、"recomposition"、Composer のフレーム | Compose / CMP | kotlin-android-kmp.md |
FlutterError、RenderFlex、package:flutter/src/...、<asynchronous suspension> | Flutter / Dart | flutter-dart.md |
Exception Type: EXC_BAD_ACCESS、Thread 0 Crashed:、.ips、libswiftCore.dylib | Swift / Apple | swift-apple.md |
thread 'main' panicked at、note: run with RUST_BACKTRACE=1、core::panicking | Rust | rust-cpp.md |
SIGSEGV、AddressSanitizer:、#0 0x...、ndk-stack、コアダンプ | C / C++ / NDK | rust-cpp.md |
Traceback (most recent call last)、at Object.<anonymous>、goroutine 1 [running] | Python / JS / Go | other-languages.md |
該当するプラットフォームのリファレンスを 1 つ読み込んでください。すべて読み込むと、コンテキストを浪費し、該当しない慣用表現が混在します。ただし、プラットフォームの境界をまたぐトレースは例外です。iOS アプリを終了させる Kotlin/Native の例外、ネイティブトレースを含む Flutter の PlatformException、JNI クラッシュなどでは、もう一方の側のリファレンスも必要になります。
2. トレースは読み取れますか、それとも先にシンボリケーションが必要ですか?
16 進数のアドレス、1 文字のクラス名(a.b.c)、<optimized out>、または行番号の欠落は、トレースが難読化されているか、シンボリケーションされていないことを示します。そのまま分析すると、自信ありげな誤った結論につながります。まずシンボリケーションを行ってください。R8 のマッピングファイル、dSYM、flutter symbolize、ndk-stack、addr2line については、symbolication.md を参照してください。
必要な成果物(マッピングファイル、dSYM、デバッグビルド)がない場合は、そのことを明確に伝え、残っているシグナル(例外の種類、スレッドの状態、トップレベルのモジュール)を抽出し、正確な診断に必要なものをユーザーに提示してもらってください。
ワークフロー
ステップ 1:エラー全体を取得する
最初の 3 行だけでなく、トレース全体を取得します。特に、次の項目を必ず確認してください。
- 例外の型とメッセージ(一字一句そのまま)
- フレーム一覧全体。すべての
Caused by:/suppressed/ネストされたチェーンを含めます - 発生したスレッド(メイン/UI スレッドの失敗とバックグラウンドでの失敗では、原因が異なります)
- コンテキスト:デバッグビルドかリリースビルドか、デバイス/OS、再現するか断続的か、発生し始めた時期
ユーザーがトレースの一部しか貼り付けていない場合は、残りを尋ねてください。重要なフレームは通常、中間部分にあります。エラーがファイルに記録されている場合は、ユーザーに貼り付けを求めず、そのファイルを読み取ります。
# Keep log reading cheap — grep for the failure region instead of loading whole files
grep -n -A 40 -iE 'exception|fatal|panic|error|traceback' <logfile> | head -120
Android の場合は、トレースを直接取得します。
adb logcat -d -b crash | tail -100
ステップ 2:必要に応じてシンボリケーションまたは難読化解除を行う
ステップ「前提条件 2」で指摘された場合にのみ行います。symbolication.md に従ってください。 読めないトレースのまま先に進まないでください。難読化されたフレームから導いた結論は、答えのように装った推測にすぎません。
ステップ 3:失敗したフレームを特定する
フレームの順序はプラットフォームによって異なります。JVM、Swift、Rust、Go、JS、ネイティブのトレースでは、例外をスローしたフレームが先頭に並びます。Python では末尾に並びます。読み始める前に順序を把握し、次の点を確認してください。
- どこで例外がスローされましたか? スロー地点に最も近い、自分のコード内のフレームです。隣接するフレームのうち、フレームワークのフレームではありません。フレームワークのフレームは仕組みを示し、自分のコードのフレームは誤りを示します。
- 最も深い
Caused by:は何ですか? 例外が連鎖している場合、最後の原因が本当の原因であることが多く、外側のラッパーは伝達のためのものです。 - どのフレームがプロジェクトに属していますか? アプリのパッケージ/モジュールのプレフィックスで絞り込みます。それ以外はすべてコンテキストです。
次に、その行にある実際のソースコードを開きます。コードがある場合、トレースだけを見て診断してはいけません。
# read the failing site plus the surrounding context
grep -n "<symbol>" -r <src-dir>
利用できる場合は、Serena や LSP などのシンボルを認識するツールを使い、ファイル全体を読むのではなく、該当する関数と呼び出し元を確認します。
ソースコードを利用できない場合(サードパーティ製アプリのトレースや、リポジトリのないコード片など)は、そのことを最初に伝え、未確認のコードに依存する結論にはすべて「可能性が高い」または「推測」と明記します。
ステップ 4:根本原因までさかのぼる
失敗した行から逆にたどり、不正な状態が生じた原因を特定します。原因候補ごとに、失敗を引き起こした具体的な値または条件を説明できなければなりません。
プラットフォームを問わず、次のような横断的な原因を検討してください。
- null/値の不在 — オプショナル値のアンラップ、nullable 値の参照、存在を前提としたマップ検索
- ライフサイクル/破棄後の使用 — 取得時点では有効だったオブジェクトが、使用時点では無効になっていた
- 並行処理 — 競合、読み取り中の値の破損、同期されていない変更、デッドロック、誤ったスレッドでの処理
- 契約違反 — API の呼び出し順序や状態が誤っている、または必須のスレッド/アクター以外から呼び出している
- リソース枯渇 — OOM、ファイルディスクリプターの枯渇、無制限のキャッシュやコレクション
- 境界の不整合 — シリアライズ、FFI、プラットフォームチャネル、JNI:境界の両側でデータの形が異なる
プラットフォーム固有のパターン一覧(握りつぶされた CancellationException、ビルド中の setState、メインアクター違反、RefCell の二重借用、解放後の使用など)は、前提条件で読み込んだプラットフォームリファレンスにあります。
次の 2 つを区別し、どちらに該当するかを明示してください。
- 直接の原因 — 例外をスローした操作(2 要素のリストに対する
list[3]) - 根本原因 — 状態が不正だった理由(API が呼び出し元の想定より少ない項目を返し、その値を検証する処理がなかった)
直接の原因だけに対する修正は、バグの修正ではなく、通常は別の場所への移動にすぎません。応急処置として提案する場合は、そのことを明記してください。
ステップ 5:修正前に仮説を検証する
仮説を反証可能な主張として明示し、その後に検証します。まずは最も手軽なテストから行います。
- 仮説を念頭に置いてコードパスを読み直し、トレース内のすべてのフレームに当てはまるか確認します。
- 同じ領域の最近の履歴を確認します:
git log -p -L <start>,<end>:<file> | head -80、またはgit log --oneline -- <file> - 疑わしい発生箇所に一時的なアサーションやログを追加して、再現します。
- クラッシュを再現する失敗するテストを書きます。これは修正後も回帰を防ぐテストとして残せるため、最良の結果です。
トレースのすべてが仮説に当てはまらない場合、その仮説は誤りです。もっともらしい説明を出して済ませるのではなく、そのことを伝えて調査を続けてください。
ステップ 6:修正案を提示する
分析は次の形式で提示します。短く、順序立てて、ざっと読めるようにしてください。
## What failed
<exception type + the failing line, as `path/to/File.kt:42`>
## Why
<root cause in 2–4 sentences: the bad state, where it came from, why it reached this line>
## Evidence
<the specific frames / code / history that support this, with file:line references>
## Fix
<the concrete change; a code sketch only when prose is not enough>
## Verify
<the command, test, or reproduction step that proves it — e.g. `./gradlew :app:testDebugUnitTest --tests '*FooTest*'`>
## Also worth checking
<other call sites with the same flaw, or the guard that would have caught this earlier — omit if none>
ユーザーが求めた場合は修正を適用します。エラーの意味だけを尋ねられた場合は、分析で終わらせてください。コードの編集を始めてはいけません。
ステップ 7:最後まで確認する
修正を適用した後は、プロジェクト独自の検証(テスト、ビルド、アナライザー)を実行し、失敗した場合も含め、実際の結果を報告します。実行していない修正は、修正ではなく提案です。
同じ種類のバグがほかにもありそうな場合は、1 行で触れてください。周辺モジュールのリファクタリングに話を広げないでください。
運用
このスキルには 3 つのモードがあります。ユーザーの依頼に応じて選択してください。
| 操作 | ユーザーの意図 | 出力 |
|---|---|---|
explain-error | 「これはどういう意味ですか」 | ステップ 1〜4。平易な説明。コードの変更なし |
analyze(デフォルト) | 「なぜクラッシュするのか」、またはトレースのみの貼り付け | ステップ 1〜6。修正案を含む完全なレポート |
fix | 「これを修正して」、「javítsd」 | ステップ 1〜7。変更を適用して検証 |
重要な制約
- フレーム、行番号、ファイルパスを決して捏造しないでください。 トレースが途中で切れている場合や難読化されている場合は、足りない情報を補わず、そのことを伝えてください。
- リリース版や難読化されたトレースを分析する前に、シンボリケーションしてください。シンボル化されていない診断は推測です。
- コードが参照できる場合は、失敗した行の実際のソースを読みます。トレースのみの分析は、コードを参照できない場合に限ります。
- 確実性のレベルを分けてください。 各結論を「確認済み(該当行を示せる)」、「可能性が高い(証拠には合致するが未検証)」、「推測(追加情報が必要)」に分類します。3 つ目を 1 つ目のように提示してはいけません。
- レポートごとに根本原因は 1 つにします。 トレースに複数の独立した障害がある場合は、まとめずに個別に分析します。
- 生ログをそのままユーザーに貼り付けないでください。 診断の根拠となる数行だけを引用します。
- 対象範囲を黙って広げないでください。 依頼されたクラッシュを修正し、関連する問題は列挙するだけにして、リファクタリングはしません。
- クラッシュログ、
.ipsファイル、マッピングファイルを外部サービスに送信してはいけません。これらにはデバイス識別子、ユーザーのパス、内部のシンボル名が含まれることがよくあります。
参考資料
- symbolication.md — プラットフォームごとの難読化解除とシンボリケーション。リリースビルドを分析する前に実施してください。
- kotlin-android-kmp.md — Kotlin/JVM、Android、コルーチン、ANR、Kotlin/Native、Compose、CMP
- flutter-dart.md — FlutterError、レンダリング/レイアウトエラー、非同期処理の境界、プラットフォームチャネル
- swift-apple.md —
.ipsクラッシュレポート、例外の種類、Swift Concurrency、Objective-C の例外 - rust-cpp.md — Rust のパニックとエラーチェーン、C++ のセグメンテーション違反、コアダンプ、サニタイザー、NDK
- other-languages.md — Python、JavaScript/TypeScript、Go、Java のみのトレース
- worked-examples.md — 各プラットフォームのサンプルトレースと完全な分析