45395 - シコウサクゴ -

5層エラーハンドリング:AIが書いたコードの「握りつぶし」問題

2026-04-03
AI駆動開発
AI駆動開発
Claude Code
エラーハンドリング
Python
品質管理
Last updated:2026-04-05
7 Minutes
1281 Words

AIが書くコードには、エラーハンドリングに関して2つの極端な傾向があります。

  1. 握りつぶし: except Exception: passで全エラーを無視する
  2. 過剰防御: あらゆる箇所にtry-exceptを入れて、本来のエラーが伝播しない

どちらも本番で「静かに壊れる」原因になります。本記事では、5層のエラーハンドリング戦略を設計し、AIが適切なレベルのエラー処理を書くための仕組みを記録します。


AIが書く典型的な「握りつぶし」

パターン1:全てをキャッチ

1
# AIが書きがちなコード
2
def fetch_data(url):
3
try:
4
response = requests.get(url)
5
return response.json()
6
except Exception:
7
return None # 何が起きても None を返す

このコードの問題点は以下の通りです。

  • ネットワークエラーなのか、JSONパースエラーなのか、認証エラーなのか区別できません
  • 呼び出し元はNoneを「データなし」と「エラー」の両方の意味で受け取ります
  • ログにも残りません

パターン2:空リストで握りつぶし

1
def get_resources():
2
try:
3
return api_client.fetch_resources()
4
except Exception:
5
return [] # エラーでも空リストを返す

これが16日間のリソース監視無効化事故の直接原因でした。認証エラーが[]に化けて、「リソースなし」として正常処理されました。


5層エラーハンドリング戦略

全体構成

1
Level 1: ロギング(全てのイベントを記録)
2
3
Level 2: 例外処理(想定されるエラーのみキャッチ)
4
5
Level 3: リトライロジック(一時的な障害に対応)
6
7
Level 4: 通知システム(人間に知らせる)
8
9
Level 5: グレースフルシャットダウン(安全に停止する)

Level 1:ロギング

1
from myModules.commonLogger import setupLogger
2
logger = setupLogger(__name__)
3
4
def process_task(task):
5
logger.info(f"[TASK] Processing: {task.name} priority={task.priority}")
6
# ... 処理 ...
7
logger.info(f"[TASK] Complete: {task.name} result={result}")

ルール: 処理の開始と終了を必ずログに記録します。ログがない区間で障害が起きると、原因の特定が困難になります。

Level 2:例外処理

1
# ❌ AIが書きがちな広すぎるキャッチ
2
try:
3
result = api_call()
4
except Exception:
5
result = default
6
7
# ✅ 想定するエラーのみキャッチ
8
try:
9
result = api_call()
10
except ConnectionError:
11
logger.warning("API接続エラー。リトライします")
12
result = retry_api_call()
13
except AuthenticationError:
14
logger.error("認証エラー。APIキーを確認してください")
15
raise # 認証エラーは上位に伝播(握りつぶさない)

ルール: except Exceptionは最上位の安全ネットとしてのみ使用します。通常の処理では具体的な例外クラスをキャッチします。

Level 3:リトライロジック

1
import time
2
3
def api_call_with_retry(func, max_retries=3, backoff_seconds=2):
4
for attempt in range(max_retries):
5
try:
6
return func()
7
except ConnectionError:
8
if attempt == max_retries - 1:
9
raise # 最終試行でも失敗なら例外を伝播
10
wait = backoff_seconds * (2 ** attempt) # 指数バックオフ
11
logger.warning(f"リトライ {attempt+1}/{max_retries}{wait}秒後)")
12
time.sleep(wait)

ルール: リトライ対象は一時的な障害(ネットワークエラー、レート制限)のみです。認証エラーやバリデーションエラーはリトライしません。

Level 4:通知システム

1
def notify_error(message, severity="warning"):
2
if severity == "critical":
3
slack.send(channel="#alerts", text=f"🚨 CRITICAL: {message}")
4
elif severity == "warning":
5
slack.send(channel="#monitoring", text=f"⚠️ WARNING: {message}")
6
# INFO レベルはSlack通知しない(ログのみ)

ルール: 通知は「人間のアクションが必要な場合」のみです。全エラーを通知すると通知疲れで見なくなります。

Level 5:グレースフルシャットダウン

1
import sys
2
3
EXIT_SUCCESS = 0
4
EXIT_PARTIAL_FAILURE = 1 # 一部失敗(処理は継続可能)
5
EXIT_ERROR = 2 # 致命的エラー(処理不可能)
6
7
def run():
8
try:
9
phase_input()
10
phase_process()
11
phase_output()
12
sys.exit(EXIT_SUCCESS)
13
except CriticalError as e:
14
logger.error(f"致命的エラー: {e}")
15
notify_error(str(e), severity="critical")
1 collapsed line
16
sys.exit(EXIT_ERROR)

ルール: 終了コードで「成功/部分失敗/致命的エラー」を区別します。プロセスマネージャーは終了コードを監視し、異常終了時にジョブを再実行できます。


CLAUDE.mdへのルール化

AIが適切なエラーハンドリングを書くように、CLAUDE.mdに以下を追加しました。

1
## エラーハンドリングルール
2
3
1. except Exception: pass は禁止
4
- 具体的な例外クラスをキャッチすること
5
- キャッチしたエラーは必ずログに記録すること
6
7
2. 戻り値での握りつぶし禁止
8
- エラー時に None や [] を返す場合は、
9
ログにWARNING以上で記録すること
10
- 認証エラーは絶対に握りつぶさない(raise すること)
11
12
3. リトライ対象の限定
13
- ConnectionError, TimeoutError のみリトライ
14
- AuthenticationError, ValueError はリトライしない

学んだこと

1. AIは「親切心」でエラーを握りつぶす

AIは「プログラムが止まらないように」という善意でエラーをキャッチします。しかし本番システムでは「止まること」より「静かに壊れること」の方が危険です。

2. 5層全てが必要

ログだけあっても通知がなければ気づけません。リトライだけあっても認証エラーには無意味です。5層を組み合わせて初めて堅牢になります。

3. 終了コードの設計がプロセスマネージャーとの連携に必須

プロセスマネージャーは終了コードに基づいてジョブの再実行を判断します。sys.exit(0)sys.exit(2)を区別することで、「正常終了」と「要再実行」を自動判別できます。


まとめ

5層エラーハンドリングで重要なのは以下の3点です。

  1. 握りつぶし禁止をCLAUDE.mdで強制: except Exception: pass禁止、認証エラーはraise必須です
  2. 5層の組み合わせ: ロギング → 例外処理 → リトライ → 通知 → グレースフルシャットダウンです
  3. 「止まる」より「静かに壊れる」が危険: エラーを隠すコードは、16日間の障害を生みます
Article title:5層エラーハンドリング:AIが書いたコードの「握りつぶし」問題
Article author:45395
Release time:2026-04-03

記事へのご質問・ご感想をお聞かせください

フィードバックを送る