Error handling robustness
Skill goonobu-dot/dev-skills-library/skills/error-handling-robustness
Robust error-handling patterns: errors as values, fail fast with assertions, no silent failures, actionable error messages. Use when writing or reviewing code with try/catch, error returns, input boundaries, or fallback logic, or when the user says エラー処理, 例外, 握りつぶし. Not for diagnosing an existing failure (use systematic-debugging) or log/monitoring design (use logging-observability).From its SKILL.md
npx -y skills add goonobu-dot/dev-skills-library --skill error-handling-robustnessAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.0 KB, ~2.4k tokens by cl100k_base, as published. Nobody here has run it
Error Handling Robustness
エラーは隠すものではなく、明示的に扱う値である。握りつぶし(silent failure)を作らない。想定内の失敗と想定外の異常を区別する。
鉄則
- すべての
catch/exceptは、最低限「ログに残す」か「呼び出し元に伝播する」のどちらかを必ず行う。 何もしない・握りつぶすcatchブロックは作らない。 - 想定内の失敗(ユーザー入力エラー、ネットワーク障害、リソース未検出等)は型付きエラー・戻り値として呼び出し側に処理させる。想定外の異常(プログラムのバグに起因するもの、到達不能なはずの分岐)はfail-fast(即座に停止・assert・例外)で処理する。
- エラーメッセージには「何が」「なぜ」失敗したかに加え、可能な限り「対処のヒント」(どの入力が問題だったか、次に何をすべきか)を含める。
- コード中の「ここでは必ずこの条件が成り立つ」という暗黙の前提は、assert・契約チェックとして明文化する。冗長に見えても書く。
作業手順
ステップ1: 「この処理は失敗しうるか」を最初に確認する
関数・エンドポイントを実装する前に、失敗しうる操作(I/O、外部呼び出し、パース、型変換、配列アクセス等)を洗い出す。失敗しうるなら、戻り値・型・例外のいずれかで明示的に表現する設計にする。
ステップ2: 想定内 vs 想定外を分類する
- 想定内(呼び出し側が対処できる): ユーザー入力の不正、ネットワークタイムアウト、リソース未検出、認可エラー → 型付きエラー・Result風の戻り値・チェック例外として返す。呼び出し側にハンドリングを強制する設計が望ましい。
- 想定外(プログラムのバグ、到達しないはずの状態): nullが来るはずのない箇所にnullが来た、switchのdefaultに来た、不変条件が破れた → assertまたは即座に例外を投げてfail-fastする。回復しようとしない。
ステップ3: catch/exceptを書くときの必須チェック
- 何かをキャッチしたら、必ず次のいずれかを行う。
- ログに構造化して残す(スタックトレース・入力値・コンテキストを含める)
- 呼び出し元に再送出する、または型付きエラーとして返す
- 明示的なフォールバック処理を行う(その場合はフォールバックした事実を必ずログに残す)
- 上記のいずれも行わない
catch { }/except: passは禁止。書きそうになったら「これは本当に無視してよいエラーか」を自問し、無視してよい場合でも理由をコメントで残す。 - エラーを再送出する際、元のエラー情報(原因・スタックトレース)を握りつぶさない(
raise from等でチェーンを保持する)。
ステップ4: エラーメッセージを実行可能にする
悪い例: "Error occurred" / "Invalid input"
良い例: "Failed to parse config.yaml: line 12, expected string but got null (key: 'timeout')"
チェック項目:
- 何が失敗したか(対象・操作)が書かれているか
- なぜ失敗したか(原因)が書かれているか
- 可能なら次に何をすべきかのヒントが書かれているか
- 機密情報(パスワード、トークン、個人情報)を含んでいないか
ステップ5: assertで仮定を文書化する
- 関数の入口で「この引数はnullではない」「このリストは空ではない」等の前提をassert/契約チェックとして書く。
- 「起こらないはず」の分岐(elseの最後、switch/matchのdefault)には、到達したら即座に失敗するassertか例外を入れる。
- リファクタリングでassertを削除する前に「本当に不要になった前提か」を確認する。多くの場合、暗黙の前提を消してしまうだけのリスクがある。
チェックリスト
- すべてのcatch/exceptがログ記録または伝播のいずれかを行っているか
- 想定内の失敗と想定外の異常を区別して処理を分けているか
- エラーメッセージに「何が・なぜ・どうすれば」が含まれているか
- 暗黙の前提条件をassertとして明文化したか
- エラー情報(原因・スタックトレース)をチェーンとして保持しているか
- リトライ・フォールバックを入れた場合、それをログに残しているか
- エラーメッセージに機密情報が漏れていないか
アンチパターン集
| やりがちな実装 | 現実・対処 |
|---|---|
catch (e) {} で何もしない | 障害調査時に何が起きたか永久に分からなくなる。最低限ログを残す |
catch (e) { console.log(e) } だけで終える | ログだけでは呼び出し元が失敗を検知できない。伝播も必要か検討する |
汎用例外Exception/Errorを握りつぶしてデフォルト値を返す | 「失敗したのに成功したように見える」状態を作る。呼び出し元に失敗を伝える設計にする |
| 全てのエラーを同じ扱いで再試行する | 想定外のバグ由来のエラーまでリトライすると無限ループや隠れた障害の温存につながる。想定内/想定外を区別する |
エラーメッセージに"error"とだけ書く | デバッグ不能。対象・原因・対処ヒントを含める |
| 「ここはnullにならないはず」と思ってチェックを省く | 暗黙の前提はassertとして明文化しないと将来壊れても気づけない |
| リファクタリング時にassertを一律削除する | assertは仕様書でもある。不要になった根拠を確認してから消す |
出典
- Rust公式ドキュメント — Error Handling(
Result<T, E>によるエラーの明示的伝播) - Go言語の
(value, error)戻り値パターンとRustの比較 — Comparing Error Handling in Rust and Go - John Carmack — assertを仮定の検証・ドキュメントとして多用する考え方(cbarrete.com/carmack.html)
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.