
AIにエラーハンドリング方針を相談する|握りつぶさない設計
「ユーザーから、保存できていないと連絡がありまして」。そう言われてログを開いたら、残っていたのは「エラーが発生しました」の一行だけだった。——この静けさ、経験があるかもしれません。
処理は落ちていません。監視も鳴っていません。画面には「完了しました」と出ていた。それなのに、データだけが入っていない。どこで失敗したのかを知る手がかりが、もうどこにも残っていない。
これは、確認が甘かったから起きるのではありません。エラー処理は「うまくいかなかったとき」にしか動かないので、普段の動作確認では一度も通らないという性質を持っているだけです。AIに書いてもらったコードなら、なおさらです。この記事では、そこをAIとどう分担するかを整理します。
結論:AIにエラーハンドリングを任せるときの要点は3つです。①コードを頼む前に、エラーの行き先を3つに仕分けて渡す——利用者に伝える/後で調べられるように残す/一時的な失敗なら立て直す。②「エラー処理を書いて」ではなく「失敗したのに、そのことが誰にも残らない箇所を挙げて」と頼む。AIは、書くことより抜けを名指しすることのほうが得意です。③失敗する経路を1つだけ、実際に起こしてみる——外部の接続を切る、必須項目を空で送る、そのどちらか1つで十分です。
AIが苦手なのは、そのエラーが起きたときに、あなたの現場では誰が何をするのかを知ることです。そこだけこちらが決めて渡せば、あとはかなり頼れます。
全部を今日やる必要はありません。まずは①の仕分けからで大丈夫です。
何が起きているのか——エラー処理は「頼まれていない仕事」になりやすい

まず、なぜここだけ勝手が違うのかを共有させてください。理由は3つあります。
1つ目は、こちらが頼んだのが「正常系」だということ。「この処理を書いて」と頼めば、AIはうまくいく道筋を書きます。エラー処理は、そのついでに添えられる部分です。ついでに添えるなら、いちばん無難な形——とりあえず捕まえて、一行だけ記録して、そのまま先へ進む——になります。手を抜いたわけではなく、どう扱ってほしいかを一言も伝えていなかっただけです。
2つ目は、捕まえる場所が決まっていないこと。ここが実は本丸です。エラーを捕まえるという操作は、それ自体が「私がこの失敗の責任を引き受けます」という宣言に近い。ところが引き受けたのに何も決められない場所で捕まえると、記録を一行残して先へ進むしかなくなります。図でいえば、行き先を決めないまま受け取ってしまった状態です。呼び出した側は、失敗があったことすら知りません。AIはファイル単位・関数単位で書くので、「上の階層が何をするか」を知らないまま、目の前で捕まえます。
3つ目は、消えた情報は後から戻せないこと。エラーには、いつ・どこで・何をしようとして失敗したのかという手がかりが付いています。捕まえて自前のメッセージに詰め替えると、この手がかりは落ちます。落ちた瞬間には誰も困りません。困るのは数週間後、障害の調査を始めたときです。冒頭の「エラーが発生しました」の一行は、こうしてできあがります。
この3つが重なると、「レビューも通り、動作確認も通り、障害のときだけ何も分からない」という状態になります。逆に言えば、エラーの行き先を先に決めて渡すだけで、かなりの部分は入口で防げるということでもあります。
具体例——AIのエラー処理は、こう抜ける
現場で見かけやすいのは、次のような形です。一つでも心当たりがあれば、この記事はそのまま使えます。
- 捕まえて、記録して、そのまま続ける:いちばん多い型です。保存に失敗したのに、処理は最後まで走りきって「完了しました」を返す。呼び出した側は成功だと受け取ります。画面の表示とデータの中身がずれるのは、たいていここです。
- 捕まえる範囲が広すぎる:
except Exceptionやcatch (Exception e)のように、何でも受け止める書き方です。通信の失敗だけを想定していたつもりが、こちらのプログラムの誤り(型の取り違え、値が入っていない、といった直すべきバグ)まで一緒に飲み込みます。バグが「一時的な不具合」の顔をして、静かに居座ります。 - 元の原因を捨てて、別のエラーに詰め替える:「保存に失敗しました」という自前のメッセージだけを投げ直す形です。何が原因で保存できなかったのかは、ここで消えます。原因をつなげて残す書き方は言語ごとに用意されていて、たとえばPythonなら
raise ... from e、Goならfmt.Errorf("...: %w", err)、JavaScriptならnew Error(msg, { cause: e })、Javaなら例外のコンストラクタに元の例外を渡します。AIは、頼めば必ず使います。頼まなければ、たいてい使いません。 - 失敗を
null・空の配列・falseで表す:呼び出した側から見ると、「見つからなかった」と「取りに行けなかった」の区別がつきません。件数0として集計されたり、空のまま上書き保存されたりします。 - 利用者に見せるものと、記録に残すものが同じ:エラーの詳細をそのまま画面に出す形です。内部のパスや問い合わせ内容、ときには接続情報まで表に出ることがあります。逆に、記録側が「失敗しました」だけになっていることも。必要な情報が、必要な側に届いていない状態です。
- やり直してはいけないものを、やり直している:入力が正しくないことが原因(HTTPで言えば400番台の多く)なら、同じものを送り直しても結果は変わりません。待って再試行する価値があるのは、混み合っている・一時的に落ちている場合(429や500番台)です。とくにAI関連のAPIは混雑で断られることがあるので、AI APIの料金とレート制限を見積もるやAI出力が不安定なときのフォールバック設計|リトライの型もあわせて見ておくと安心です。なお、再試行が二重登録につながる話はAIに非同期・並行処理を書かせる|競合とデッドロックの確認にまとめてあります。
- 途中で失敗したときの後始末が抜けている:開いた接続、確保したロック、始めたトランザクション。うまくいった道では必ず閉じているのに、失敗した道だけ閉じ忘れることがあります。これは動作確認では通らない経路なので、目で追うしかありません。
どれも、注意力の問題ではありません。これらは「動いてしまう」ことが特徴なので、動かして確かめるという普段のやり方では見つからない、というだけです。見つけにくいものを相手にしている——そう捉えるほうが、実態に近いと思います。
影響——困るのは、いちばん急いでいるとき
エラー処理の抜けが少し特別なのは、平時にはまったく害がないところです。顔を出すのは、決まって障害の最中です。
- 調査が「何も残っていない」から始まる:原因を探す前に、まず情報を足すところから始めることになります。再現しない不具合の場合、ここで数日が消えます。
- データが中途半端な形で残る:3つのうち2つだけ書けて、3つ目で失敗して、戻していない。落ちるより、半分成功しているほうが厄介です。あとで数を合わせる作業が発生します。
- 利用者にだけ見えている失敗がある:こちらの監視は静かなのに、問い合わせだけが来ます。「こちらでは正常に見えています」と答えてしまい、話がかみ合わなくなることもあります。
- アラートが鳴らない:例外が消えているので、エラー率としても数えられません。気づく仕組みそのものが効かなくなるのが、握りつぶしのいちばん静かな影響です。記録と指標の置き方はAI機能の効果測定|導入後に見るログと指標の設計も参考になります。
ただ、ここは強調しておきたいのですが、この種の抜けは、書く前なら本当に安く塞げます。あとから探すのは大変でも、先に「このエラーは誰に何を伝えるのか」を問うのは1分です。いま実装の手前でこの記事を読んでいるなら、それはいちばん安いタイミングにいるということです。
明日からやること(3ステップ)
大がかりな設計変更は要りません。この順番で、今日から小さく始められます。
1. AIに頼む前に、エラーの行き先を3つに仕分ける
いきなり「エラー処理も入れて」と頼まないほうが、返ってくるものの質が変わります。先に、想定する失敗を次の3つに分けます。図の3つの列がそのまま対応します。
①伝える:利用者が自分で直せる失敗(入力の不備、権限がない、期限切れ)。→ 何が起きたかより、次に何をすればいいかを書く。
②残す:想定していなかった失敗(プログラムの誤り、前提が崩れた)。→ 処理は止めて、原因ごと記録して、上へ渡す。
③立て直す:一時的な失敗(通信が切れた、混み合っている、相手が一時的に落ちている)。→ 少し待って数回だけ再試行し、それでもだめなら②へ落とす。
そのうえで、もう1行だけ決めます。捕まえるのは、そのエラーについて何かを決められる場所だけ。決められないなら、捕まえずに上へ渡す。これは一行ですが、いちばん効きます。
例:
- 決済APIのタイムアウトは③。2回まで待って再試行し、それでもだめなら②に落として注文は確定させない
- 入力された郵便番号の形式ちがいは①。「7桁の数字で入力してください」と画面に出す
- 想定していないデータ形式が返ってきたら②。処理を止めて、返ってきた内容ごと記録する
- 捕まえるのは注文処理の入口だけ。途中の関数では捕まえずに上へ渡す
書いてみると分かるのですが、難しいのは③と②の線引きです。ここが埋まらないまま「エラー処理も入れて」と頼むと、AIはすべてを③のように——つまり、何となく続行するように——扱います。この仕分けは、AIが守るべき線をこちらから示す唯一の材料だと思ってください。この数行が、そのまま渡す素材になります。前提や制約の渡し方そのものはAIにコードを書かせる前に渡す前提と制約|伝え方の型と同じ考え方です。
2. 「握りつぶしている箇所」を名指しで挙げさせる
仕分けを渡したうえで、実装ではなく、抜けを先に聞きます。これはAIがかなり得意な仕事です。
そのまま使える頼み方の例:
以下のコードと前提について、修正案は出さなくて結構です。 「失敗したのに、そのことが呼び出し元にも記録にも残らない箇所」を挙げてください。それぞれ、①どんな失敗が起きたときか ②その事実がどこで消えるか ③消えると、後の調査で何が分からなくなるか、の3点で書いてください。とくに次の5点を名指しで見てください。
1. 捕まえたあとに処理を続けている箇所(呼び出し元が成功だと受け取らないか)
2. 捕まえる範囲が広すぎる箇所(プログラムの誤りまで飲み込んでいないか)
3. 元の原因を捨てて、別のエラーに詰め替えている箇所
4.null・空・falseで失敗を表している箇所(呼び出し元が正常と区別できるか)
5. 途中で失敗したときに、後始末(接続・ファイル・ロック・トランザクション)が漏れる箇所
あわせて、利用者に見せる文言と、記録に残す内容が同じになっている箇所も指摘してください。
前提(エラーの行き先):
(ここに1で書いた仕分け)
コード:
(ここに対象のコード)
ポイントは、「修正案は出さなくて結構です」と先に伝えることです。これを言わないと、AIは指摘のあとに親切な修正コードを並べてくれて、そちらに目が行きます。いま欲しいのは、自分では見えていない「消えている場所」です。設計の抜けを洗わせる問い方は設計レビューの見落としをAIに洗わせる|プロンプトの型にも型をまとめています。
返ってきた指摘は、そのまま信じずに1つずつ「これは実際に困るか」を確かめてください。AIは、社内向けの小さなバッチにまで、利用者向けの丁寧なエラー表示を求めてくることがあります。全部に対応する必要はありません。 困るものだけ拾えば十分です。選り分け方はAIレビューの指摘を取捨選択する判断軸|直す・保留・落とすが使えます。
直し方まで進むなら、選択肢はそう多くありません。何も決められない場所では捕まえない(いちばん安い)、捕まえるなら元の原因を連れて上げる(前述の from e や %w、cause)、利用者向けの文言と記録用の詳細を分ける。3つ目をやるときは、問い合わせ番号のような短い識別子を1つ振って、画面と記録の両方に載せておくと、あとから一発でたどれます。「先ほどのエラー画面に出ていた番号を教えてください」で調査が始められるのは、かなり楽です。
3. 失敗する経路を、1つだけ実際に起こす
直したあと、直ったことをどう確かめるかが最後の壁です。ここも1つだけで構いません。エラー処理は、意図的に壊さないと一度も動かない部分です。
- 外部との接続を切ってみる:接続先の設定をわざと違うものにする、テスト用に用意した停止状態を使う、ネットワークを一時的に落とす。どれでも構いません。
- 入力を1つ壊してみる:必須項目を空で送る、想定外の形式を送る。①の道が通るかを見ます。
- 途中で失敗させるテストを1本だけ書く:2つ目の保存だけが失敗するようにして、1つ目が残っていないかを確かめます。後始末の抜けは、ここでしか見つかりません。
見るのは3つだけです。利用者の画面に何が出るか/記録に何が残るか/データが中途半端になっていないか。とくに2つ目は、実際に自分で読んでみてください。「この一行だけで、3週間後の自分は原因にたどり着けるか」。たどり着けないと感じたら、足りないのは情報です。落ちたあとの調査そのものを速くする話はスタックトレースをAIに読ませる|バグ調査を速くする渡し方にまとめてあります。
3つとも今日やる必要はありません。ステップ1の仕分けを書くだけでも、AIから返ってくるコードは変わります。
エラーハンドリングのチェックリスト
全部に○が要るわけではありません。気になった行だけで十分です。
① AIに頼む前に
- 想定する失敗を「伝える/残す/立て直す」に仕分けたか
- 再試行してよい失敗と、してはいけない失敗を分けたか
- どこで捕まえるか(=どこで何を決めるか)を決めたか
- 利用者に見せてよい情報の範囲を決めたか
② 返ってきたコードで
- 捕まえたあとに、そのまま処理を続けている箇所はないか
- 捕まえる範囲が広すぎて、プログラムの誤りまで飲み込んでいないか
- 元の原因を捨てずに、つなげて上へ渡しているか
- 失敗を
null・空・falseで表していないか(正常と区別できるか) - 利用者向けの文言と、記録用の詳細が分かれているか
- 再試行に回数と待ち時間の上限があるか(無限に待ち続けないか)
- 途中で失敗したとき、接続・ロック・トランザクションの後始末が漏れないか
③ 出す前に
- 失敗する経路を1つ、実際に起こして確かめたか
- 記録された内容だけで、3週間後に原因までたどり着けそうか
- 画面と記録をつなぐ識別子(問い合わせ番号など)があるか
- 失敗が起きたことに、監視やアラートが気づける形になっているか
- 記録に、見せてはいけない情報が混ざっていないか
この記事のまとめ
AIはエラー処理のコードを、驚くほど速く、それらしく書きます。得意なのは書くことと、抜けを名指しすること。苦手なのは、そのエラーが起きたときに、あなたの現場で誰が何をするのかを知ることです。それは、渡していない情報だからです。
やることは3つだけです。①エラーの行き先を「伝える・残す・立て直す」に仕分けて渡す/②「修正案は出さなくていいので、失敗が誰にも残らない箇所を挙げて」と頼む/③失敗する経路を1つだけ実際に起こす。この3つがあると、エラー処理は「祈りながらリリースする作業」から、「決めて確かめる作業」に近づきます。

エラー処理が後回しになりやすいのは、それが「起きてほしくないこと」のための仕事だからだと思います。起きないことを願いながら備えるのは、地味で、誰にも褒められにくい。でも、障害の夜にログを開いた誰かを助けるのは、その地味な備えだけです。今日ひとつ「これが失敗したら、誰に何が伝わるだろう」と問えたなら、それだけで、未来の自分の調査を何時間か短くしています。
よければ、こちらも
- そもそも任せる範囲を切りたいときはAIコーディングは小さく切る|関数1個から始めるスコープ設計
- 返ってきたコードをまとめて確かめたいときはAI生成コードのレビュー・検証チェックリスト|使う前に見る所
- 動作確認の型から整えたいときはAI生成コードの動作確認|そのまま使わない最低限の型
- 異常系のテストが薄いと感じたら境界値・異常系をAIに考えさせて漏れを減らす手順