---
paths: ["src/**"]
---

# エラーハンドリング方針 (Error Handling)

例外を raise / 捕捉するコード（`src/**`）を書くときは本ルールに従うこと．コーディング全般の規約は `.claude/rules/coding-style.md` を参照する．

## 例外の使い分け (Exception Types)

| 状況 | 送出する例外 |
| --- | --- |
| 利用者の入力（設定ファイル・データセットの定義・取り込み元の構成・バンドル等）の内容・形式の問題 | `ValueError` に統一して raise |
| 内部論理エラー（到達しないはずの分岐・不変条件違反等） | `RuntimeError` |
| pydantic を使うバリデーション | `pydantic.ValidationError` をそのまま伝播（握りつぶさない） |
| SQLite の制約違反（`sqlite3.IntegrityError`） | 意味のある `ValueError` に変換して再送出する（`from` で元の例外を残す） |

## データの問題は止める (Fail Fast on Data)

目録は再現性の根拠になるため，**静かな除外・静かな上書きをしない**．

- 取り込み元の想定外の構成・未知の所見の値・ハッシュの不一致・参照切れは raise する
- 「とりあえず `try` で囲んで `continue`」は禁止．登録件数が静かに変わり，版の中身の解釈を誤る
- 例外: `tiasdata check`・`tiasdata stats` は現状把握のためのコマンドなので，問題を数えて最後に報告し，終了コードを 0 以外にする（止めずに全体を見る）

## 個人情報を出さない (No PII in Messages)

例外メッセージ・ログに，実際の患者 ID・対応表の中身・元のファイル名を含めない．位置は研究用 ID・SHA-256・件数・取り込み元の名前で示す．

```python
raise ValueError(f"取り込み元 '{source}' のフォルダ {folder_index} 番目: 想定外の拡張子のファイルが {count} 件ある")
```

- 元のファイル名が無いと原因を特定できない場合だけ，CLI に `--show-source-names` のような明示の指定があるときに限り表示する

## CLI の終了コード (Exit Codes)

| 終了コード | 意味 |
| --- | --- |
| 0 | 成功 |
| 1 | 入力・データの問題（`ValueError`）．メッセージを標準エラーに 1 行で出す（スタックトレースは出さない） |
| 2 | 引数の誤り（`argparse` の既定） |
| それ以外の例外 | スタックトレースを出して異常終了する（バグとして扱う） |

## 方針 (Policy)

- **最小限主義**: 独自例外クラスの階層は導入しない．上表の標準例外で表現できる範囲に留める
- **メッセージは日本語可**: 原因と対処が分かる具体的な内容にする
- **握りつぶさない**: `except:` で例外を無言で捨てない
- **境界で検証する**: 入力の検証は入口（設定の読み込み・取り込み元の走査・定義の読み込み・バンドルの読み込み）で行い，内部は検証済みの入力が来る前提で書く

## 参考 (References)

- [Python 公式ドキュメント — 組み込み例外](https://docs.python.org/3/library/exceptions.html)
- [Pydantic — Error Handling](https://docs.pydantic.dev/latest/errors/errors/)
