エラーメッセージの読み方
目次
- 概要
- エラーを分解する
- stack trace
- 原因の連鎖を読む
- exit code
- errnoとOSエラー
- HTTP error
- networkとTLSのエラー
- compiler error
- language別の読み方
- package managerとbuildのエラー
- containerとKubernetesのエラー
- cloudと権限エラー
- 原因調査の手順
- 質問・報告の書き方
- エラー分類マップ
- 最小再現を作る
- エラーをよくする書き方
- エラーの構造化と収集
- 言語別エラーの特徴
- エラーハンドリングのアンチパターン
- エラーハンドリングのベストプラクティス
- エラー調査の実践的フロー
- デバッグツール活用
- エラーとモニタリングの統合
- まとめ
- 参考文献
概要
エラーメッセージは、失敗の原因を探すための地図です。怖いものではなく、システムが「どこで、何が、なぜ失敗した可能性があるか」を教えてくれる情報です。
エラーは、種類、場所、原因、再現条件に分けて読みます。全文を読む前に、最初のエラー、最後のエラー、ファイル名、行番号、status code、exit codeを確認すると切り分けやすくなります。
エラーを分解する
エラーには、いくつかの情報が含まれます。
| 情報 | 例 |
|---|---|
| 種類 | TypeError, SyntaxError, AccessDenied |
| 場所 | src/app.ts:42 |
| 対象 | config.json, S3 bucket |
| 理由 | permission denied, undefined is not a function |
| 文脈 | stack trace, request id |
| 操作 | read, connect, parse, compile |
| 期待値 | expected number, got string |
| 実際値 | undefined, null, empty response |
| 対処のヒント | run npm install, check IAM policy |
まず見る順番は、だいたい次です。
| 順番 | 見るもの | 例 |
|---|---|---|
| 1 | エラー種別 | TypeError, ENOENT, AccessDenied |
| 2 | 一番具体的な文 | No such file or directory |
| 3 | 場所 | file、line、function、endpoint |
| 4 | 入力 | command、request、config、data |
| 5 | 直前の変更 | dependency update、環境変数、権限変更 |
| 6 | 関連ID | request id、trace id、job id |
長いエラーでは「最初」と「最後」の両方を見ます。最初のエラーは根本原因に近いことが多く、最後のエラーは実際に処理が止まった場所を示すことが多いです。
Caused by: ENOENT: no such file or directory, open './config.json'
...
Error: failed to start server
この場合、最後の failed to start server だけを見ると曖昧です。config.json を開けなかったことが、より具体的な原因です。
stack trace
stack traceは、関数呼び出しの履歴です。
TypeError: user.name is undefined
at renderUser (src/user.ts:12)
at renderPage (src/page.ts:45)
at main (src/main.ts:3)
読む順序です。
- エラー種別を見る
- メッセージを見る
- 自分のコードの最初の行を探す
- その関数に渡された値を確認する
- 直前の変更を確認する
ライブラリ内部の行が多くても、必ずしもそこが原因とは限りません。自分のコードから不正な値を渡していることも多いです。
stack traceは上から読むか下から読むかで迷います。多くの場合、上の方に「実際に例外が投げられた場所」があり、下の方に「そこへ至った入口」があります。
TypeError: Cannot read properties of undefined (reading 'name')
at renderUser (src/user.ts:12:21)
at renderPage (src/page.ts:45:10)
at handleRequest (src/server.ts:88:5)
この例では、renderUser が落ちた場所、handleRequest が入口です。修正のためには renderUser の12行目を見るだけでなく、renderPage がどんな user を渡したかも見ます。
stack traceを読むときのコツです。
| 観点 | 見ること |
|---|---|
| 自分のコード | src/ やproject pathの最初の行 |
| ライブラリコード | 呼び出し方が間違っていないか |
| async境界 | callback、Promise、task、thread |
| generated code | transpile後ではなくsource mapや元ファイル |
| 省略表示 | ... の前後に重要な行がないか |
原因の連鎖を読む
よいエラーは、低レベルの原因に高レベルの文脈を重ねています。
Error: failed to load application config
Caused by: failed to read ./config/production.json
Caused by: No such file or directory (os error 2)
この形では、下に行くほど低レベル、上に行くほどアプリケーション上の意味に近くなります。
原因連鎖を見るときは、「最も低レベルの原因」と「ユーザー影響に近い失敗」の両方をメモします。
| 見るもの | 例 | 役割 |
|---|---|---|
| root cause | ENOENT |
直接直す対象 |
| context | load application config |
何の処理中か |
| impact | server startup failed |
何ができなかったか |
Rustの anyhow やGoのwrapped errorのように、低レベルエラーへ文脈を足していく文化があります。読む側は「最後に表示された1行」だけでなく、Caused by、source、wrapped、because を探します。
exit code
CLIでは、終了状態がexit codeで返ります。
| code | よくある意味 |
|---|---|
| 0 | 成功 |
| 1 | 一般的な失敗 |
| 2 | usage error |
| 126 | 実行不可 |
| 127 | コマンドが見つからない |
| 130 | Ctrl-C |
npm run build
echo "$?"
CIでは、exit codeが0以外だとjobが失敗します。
shellでは、失敗しても後続コマンドが動くことがあります。
false
echo "still running"
CIやscriptでは、必要に応じて set -euo pipefail のような設定を使います。ただし、これも万能ではありません。pipeの途中で失敗したコマンド、条件分岐内のコマンド、意図的に失敗を許すコマンドを区別します。
set -euo pipefail
curl -fsS https://example.com/health
exit codeを読むときは、次を確認します。
| 観点 | 例 |
|---|---|
| どのコマンドが失敗したか | npm run build の中の tsc |
| signalで止まったか | 130 はCtrl-C、137 はkill/OOMのことがある |
| usage errorか | 引数やoptionの間違い |
| retry可能か | network timeout、rate limit |
| deterministicか | 毎回同じ入力で落ちるか |
errnoとOSエラー
OSやruntime由来のエラーでは、ENOENT、EACCES、ECONNREFUSED のようなerrno名が出ます。数値は環境差があるため、基本的には名前で読みます。
| errno | 意味 | 最初に見るもの |
|---|---|---|
ENOENT |
ファイルやディレクトリがない | path、cwd、生成手順 |
EACCES |
権限がない | file permission、実行ユーザー |
EPERM |
操作が許可されていない | 権限、sandbox、capability |
EADDRINUSE |
portが使用中 | 起動済みprocess、port設定 |
ECONNREFUSED |
接続先が拒否 | service起動、host/port |
ECONNRESET |
接続が途中で切れた | peer、proxy、timeout |
ETIMEDOUT |
timeout | network、DNS、firewall |
ENOSPC |
disk容量不足 | disk、inode、quota |
EMFILE |
開けるfile descriptor上限 | leak、ulimit、connection数 |
ENOENT は「そのファイルがない」とは限りません。pathの途中のディレクトリがない、cwdが想定と違う、container内にcopyされていない、symlinkが切れている、という場合もあります。
pwd
ls -la
stat ./config.json
EACCES と EPERM は似ています。EACCES はファイル権限や実行権限の不足、EPERM はより広く「操作自体が許可されていない」場面で出ます。container、macOS sandbox、Linux capability、cloud IAMが絡むと EPERM 的な問題に見えることがあります。
HTTP error
HTTPではstatus codeが大きな手がかりです。
| code | 読み方 |
|---|---|
| 400 | requestの形が悪い |
| 401 | 認証が必要 |
| 403 | 権限がない |
| 404 | 対象がない |
| 429 | rate limit |
| 500 | server内部エラー |
| 502 | gateway/proxyの先で失敗 |
| 503 | 一時的に利用不可 |
403 と 404 は似て見えますが、原因が違います。権限問題なのか、URLやroutingの問題なのかを分けます。
HTTP errorは、status codeだけでなく、method、URL、response body、response headerを一緒に見ます。
curl -i https://example.com/api/users
curl -v https://example.com/api/users
| 見るもの | 例 | 分かること |
|---|---|---|
| method | GET, POST |
routingやCORSの違い |
| URL | path、query | typo、base URL、environment |
| status | 401, 403, 404 |
大まかな分類 |
| body | JSON error code | application固有の理由 |
| header | www-authenticate, retry-after |
認証方式、rate limit |
| request id | x-request-id |
server側ログへの入口 |
同じ 403 でも、applicationの権限不足、cloud storageのbucket policy、WAF block、CORS preflightの失敗など、層が違うことがあります。
networkとTLSのエラー
network errorは、アプリケーションのエラーに見えても、DNS、TCP、TLS、proxy、serverのどこでも起きます。
| エラー | 典型的な意味 | 確認 |
|---|---|---|
Could not resolve host |
DNS解決できない | hostname、DNS、VPN |
Connection refused |
接続先portで待ち受けがない | service起動、port |
Connection timed out |
到達できない、応答がない | firewall、route、security group |
TLS handshake failed |
TLS交渉失敗 | 証明書、SNI、protocol |
certificate has expired |
証明書期限切れ | certificate chain |
self signed certificate |
信頼されない証明書 | CA bundle、開発環境 |
429 Too Many Requests |
rate limit | Retry-After、backoff |
切り分けは、層を下から見ます。
dig example.com
curl -v https://example.com/
openssl s_client -connect example.com:443 -servername example.com
curl -v は、DNS、TCP、TLS、HTTPのどこまで進んだかを見やすくしてくれます。本文よりも、接続ログとheaderが重要なこともあります。
compiler error
compiler errorは、行番号と型の情報をくれます。
error TS2322: Type 'string' is not assignable to type 'number'.
型エラーでは、次を見ます。
- 期待されている型
- 実際に渡した型
- 型が決まった場所
- genericやunionの広がり
最初のエラーが原因で、後続のエラーが連鎖していることがあります。まず最初のエラーから直します。
compiler errorは、runtime errorより親切なことが多いです。型、期待値、実際値、候補、修正案が含まれます。
src/app.ts:12:7 - error TS2322: Type 'string' is not assignable to type 'number'.
読む要素です。
| 要素 | 例 | 意味 |
|---|---|---|
| file/line | src/app.ts:12:7 |
直接見る場所 |
| code | TS2322 |
検索しやすいID |
| expected | number |
要求されている型 |
| actual | string |
渡した型 |
| related info | 別ファイルの型定義 | 型が決まった場所 |
型エラーでは「エラーが出た場所」と「型が決まった場所」が違うことがあります。呼び出し側、型定義、genericの推論、設定ファイルを順番に見ます。
language別の読み方
言語ごとに、エラーの出方には癖があります。
| 言語 | よく見るもの | 読み方 |
|---|---|---|
| JavaScript | TypeError, ReferenceError, SyntaxError |
undefined、非同期境界、ブラウザ/Node差を見る |
| TypeScript | TSxxxx |
error code、期待型、実際型、型定義元を見る |
| Python | exception class、traceback | 一番下の例外と自分のコードの行を見る |
| Node.js | code: ENOENT など |
JS errorとOS errnoの両方を見る |
| Go | error return、wrapped error |
%w で包まれた原因、errors.Is/As 的な分類を見る |
| Rust | Result, panic, backtrace |
recoverable/unrecoverable、Caused by、RUST_BACKTRACE を見る |
| Java | exception、Caused by |
最初の Caused by と自分のpackageの行を見る |
Python tracebackの例です。
Traceback (most recent call last):
File "app.py", line 10, in <module>
main()
File "app.py", line 6, in main
load_config("config.json")
FileNotFoundError: [Errno 2] No such file or directory: 'config.json'
最後の FileNotFoundError が直接原因です。ただし、load_config を呼んだ文脈も見る必要があります。
Rustでは、panicとrecoverable errorを分けて考えます。
thread 'main' panicked at src/main.rs:10:5:
index out of bounds
panicはバグ寄り、Result で返るerrorは入力や環境の失敗として扱えることが多いです。必要なら RUST_BACKTRACE=1 で追加情報を出します。
package managerとbuildのエラー
build errorは、コードそのものではなく、dependency、lockfile、cache、Node/Python/Rust/Goのversion、OS packageの不足で起きることがあります。
| 症状 | よくある原因 | 見るもの |
|---|---|---|
command not found |
tool未install、PATH違い | which, PATH, package scripts |
| lockfile不一致 | install方法の違い | package-lock.json, pnpm-lock.yaml |
| native build失敗 | compilerやheader不足 | OS package、Python、node-gyp |
| module not found | dependency不足、alias設定 | package.json、tsconfig、bundler |
| version mismatch | runtime違い | .node-version, engines, CI image |
| cache由来の失敗 | 古いartifact | clean install、cache key |
build logは長くなりがちです。最初に出た error、ERR!、Caused by、failed to を探します。
rg -n "error|ERR!|failed|Caused by" build.log
同じlogに warning が大量にあっても、失敗を決めたのは最後のexit codeと、その直前の明確なerrorであることが多いです。
containerとKubernetesのエラー
containerでは、アプリケーションエラーに見えても、image、command、env、volume、network、権限が原因のことがあります。
| エラー | よくある原因 | 確認 |
|---|---|---|
CrashLoopBackOff |
起動してすぐ落ちる | kubectl logs --previous |
ImagePullBackOff |
image取得失敗 | image名、tag、registry権限 |
ErrImagePull |
pullできない | secret、network、存在確認 |
CreateContainerConfigError |
env/secret/config参照ミス | describe pod |
OOMKilled |
memory不足 | limit、メモリ使用量 |
| permission denied | user/volume権限 | USER, securityContext |
Kubernetesでは logs と describe をセットで見ます。
kubectl logs pod/api-xxxxx
kubectl logs pod/api-xxxxx --previous
kubectl describe pod/api-xxxxx
logs はアプリケーションの出力、describe はscheduler、image pull、probe、OOM、eventを見る入口です。
cloudと権限エラー
cloudのエラーは、resource、principal、action、conditionに分解します。
AccessDenied: User is not authorized to perform: s3:PutObject on resource ...
| 要素 | 見るもの |
|---|---|
| principal | 誰が実行しているか |
| action | 何をしようとしたか |
| resource | どのresourceか |
| condition | IP、VPC、tag、region、MFAなど |
| boundary | SCP、permission boundary、bucket policy |
AWS S3のようなサービスでは、HTTP statusに加えてservice固有のerror codeが返ります。AccessDenied、NoSuchBucket、NoSuchKey、InvalidAccessKeyId のようなcodeは、HTTP statusより具体的です。
権限エラーでは、credentialが想定通りかを先に確認します。
aws sts get-caller-identity
aws configure list
「権限がない」の前に、「そもそも違うアカウント/roleで実行している」こともよくあります。
原因調査の手順
調査では、同時に多くを変えないことが重要です。1つ変えて、結果を見る。これを繰り返します。
調査ログを残すと、迷子になりにくくなります。
| 時刻 | 仮説 | 試したこと | 結果 | 次 |
|---|---|---|---|---|
| 12:10 | config不足 | envを確認 | API_URL 未設定 |
.env とCI secretを見る |
| 12:20 | 権限不足 | caller identity確認 | 想定外role | OIDC role設定を見る |
「直したつもりで別のものを壊した」を避けるため、再現コマンドを固定してから変更します。
npm run build
同じコマンドを繰り返し、出力がどう変わったかを見るだけで、かなり調査が安定します。
質問・報告の書き方
質問するときは、相手が再現できる情報を渡します。
- 何をしようとしたか
- 期待した結果
- 実際の結果
- エラーメッセージ全文
- 実行したコマンド
- 環境
- 直前に変えたこと
- 試したこと
悪い例です。
動きません。
良い例です。
npm run buildでdistを生成したいです。
期待: build成功
実際: TypeErrorが発生
環境: Node.js 22, macOS
直前の変更: build/build.jsのリンク生成を変更
さらに良い報告では、再現手順と全文ログを分けます。
目的:
dist/ を生成したい
再現手順:
1. npm ci
2. npm run build
期待:
buildが成功し、dist/index.htmlが生成される
実際:
build/build.js:120でTypeError
エラー全文:
TypeError: Cannot read properties of undefined (reading 'href')
at buildNavigation (build/build.js:120:18)
環境:
Node.js 24.4.1
macOS
試したこと:
- node_modulesを削除してnpm ci
- build/build.jsの直前変更を確認
質問や報告の目的は、詳しく見せることではなく、相手が同じ状態を再現できるようにすることです。ログ全文は長くてもよいですが、本文では重要な行を先に抜き出します。
エラー分類マップ
エラーは、まず種類で分けると調査しやすくなります。
| 分類 | 典型例 | 最初に見るもの |
|---|---|---|
| syntax / compile | typo, 型不一致 | ファイル名、行番号 |
| runtime | null, undefined, panic | stack trace |
| network | timeout, DNS, TLS | URL, status, retry |
| permission | AccessDenied, 403 | IAM, token, role |
| configuration | env不足, path違い | config, env, cwd |
| data | parse失敗, schema不一致 | 入力データ |
分類できるだけで、見るべき資料がかなり絞れます。
もう少し実務寄りに分けると、次のようになります。
| 分類 | 質問 |
|---|---|
| 自分のコード | 直前の変更で壊したか |
| 入力データ | 特定の入力だけで落ちるか |
| 設定・環境 | localとCI、本番で差があるか |
| 依存ライブラリ | version updateがあったか |
| 外部サービス | timeout、rate limit、障害情報はあるか |
| OS / runtime | path、permission、version、resource不足か |
| 権限 | 誰が、何に、どのactionをしたか |
この分類に沿って「同じコードで環境を変える」「同じ環境で入力を変える」のように1軸ずつ動かすと、原因に近づきやすくなります。
最小再現を作る
原因が分からないときは、最小再現を作ります。最小再現とは、問題を起こすために必要な最小のコード、入力、手順です。
作り方です。
- エラーメッセージ全文を保存する
- 入力を小さくする
- 関係ない依存を外す
- 1ファイルで再現できるか試す
- 成功するケースと失敗するケースを並べる
最小再現は、質問のためだけでなく、自分が原因を理解するためにも強力です。削っている途中で原因が見つかることも多いです。
最小再現では、次を削ります。
| 削るもの | 例 |
|---|---|
| UI | buttonや画面全体ではなく、関数呼び出しにする |
| network | mock responseにする |
| database | fixture JSONにする |
| framework | plain scriptで再現する |
| 大量データ | 1件の入力にする |
| 環境差 | Dockerやversion固定でそろえる |
失敗する例と成功する例を並べると、差分が原因候補になります。
// 成功
parseUser({ name: "Alice" })
// 失敗
parseUser({})
この2つの差は name の有無です。巨大なアプリ全体では見えなかった原因が、小さい入力だと見えます。
最小再現を作るときの落とし穴です。
| 落とし穴 | 対策 |
|---|---|
| 削りすぎて再現しなくなる | 直前に戻して、最後に削った要素を見る |
| 環境依存を残す | version、OS、envを明記する |
| 非同期や時刻依存を見落とす | seed、clock、timeoutを固定する |
| logを加工しすぎる | 原文を保存してから要約する |
エラーをよくする書き方
エラーは読むだけでなく、書く側でも改善できます。よいエラーは、原因、対象、文脈、次の行動を含みます。
悪い例です。
failed
少し良い例です。
failed to read config
さらに良い例です。
failed to read config file "./config/production.json": no such file or directory
エラーを書くときの要素です。
| 要素 | 例 |
|---|---|
| 操作 | read, parse, connect, authorize |
| 対象 | file path, URL, resource id |
| 期待 | expected JSON object |
| 実際 | got empty file |
| 原因 | permission denied |
| 文脈 | while loading production config |
| 次の行動 | check CONFIG_PATH |
エラーに文脈を追加する例です。
try {
const raw = await fs.readFile(configPath, "utf8");
return JSON.parse(raw);
} catch (error) {
throw new Error(`failed to load config from ${configPath}`, { cause: error });
}
ユーザー向けメッセージと開発者向けメッセージは分けます。
| 対象 | 内容 |
|---|---|
| ユーザー | 次にどうすればよいか、入力をどう直すか |
| 開発者 | stack trace、request id、内部error code |
| 運用者 | service、resource、権限、外部依存 |
security的には、内部情報を出しすぎないことも大切です。
| 出してよい | 避ける |
|---|---|
| request id | password、token |
| validation error | SQL全文 |
| 入力項目名 | secret入りURL |
| 問い合わせ先 | stack traceを一般ユーザーへ表示 |
Web APIでは、内部ログには詳細を残し、clientには安定したerror codeとrequest idを返す設計が扱いやすいです。
{
"error": {
"code": "CONFIG_NOT_FOUND",
"message": "configuration is missing",
"request_id": "req-123"
}
}
内部ログには、より詳しい原因を残します。
{
"level": "ERROR",
"event": "config_load_failed",
"request_id": "req-123",
"path": "./config/production.json",
"error.type": "ENOENT",
"message": "failed to load config file"
}
エラーの構造化と収集
本番環境では、ばらばらなエラーログでは調査が困難です。エラーを構造化して収集する仕組みが重要です。
エラートレーシング (OpenTelemetry)
OpenTelemetry標準を使うと、trace ID → span ID → event として
分散システムでエラーを追跡できます
request-id: req-abc123
├─ span: receive_request (duration: 1ms)
├─ span: database_query (duration: 50ms) ← エラー発生
│ └─ event: connection_timeout
│ └─ error.type: ETIMEDOUT
│ └─ error.message: "failed to connect to postgres:5432"
└─ span: error_response (duration: 2ms)
Jaeger、Datadog、New Relicなどが実装。
エラー分類と監視
エラーの層別:
1. User error (400系) → ドキュメント改善
2. Server error (500系) → 高優先度アラート
3. Transient (timeout等) → リトライ設定確認
4. Permanent (permission等) → 設定・権限確認
ログ検索パターン
実務でよく必要になる検索:
# 特定のuser_idが失敗している場合
user_id: "user-123" AND level: ERROR
# 特定のリソースで詳細ログを取る
resource_id: "resource-456" AND detailed_log: true
# 指定時間帯でのエラートレンド
@timestamp: [2024-01-15T10:00 TO 2024-01-15T11:00]
AND error.type: ECONNREFUSED
# 特定のスタックトレースパターン
stack_trace: "*ConnectionPool*" AND "*timeout*"
Elasticsearch、Splunk、Cloud Loggingなどのクエリ言語を習得すると、トラブルシューティング速度が大幅向上。
言語別エラーの特徴
Rustのエラー処理
Rustは Result<T, E> 型で明示的なエラー処理を強制:
fn parse_config(path: &str) -> Result<Config, ConfigError> {
let content = std::fs::read_to_string(path)
.map_err(|e| ConfigError::FileNotFound(path.to_string(), e))?;
let config = serde_json::from_str(&content)
.map_err(|e| ConfigError::ParseError(e.to_string()))?;
Ok(config)
}
// 呼び出し側
match parse_config("config.json") {
Ok(cfg) => { /* process */ },
Err(ConfigError::FileNotFound(path, _)) => eprintln!("Missing: {}", path),
Err(ConfigError::ParseError(msg)) => eprintln!("Invalid JSON: {}", msg),
}
Rustのコンパイラは Result を無視できず、エラーハンドリングを忘れない設計。
Pythonの例外処理
try:
with open("config.json") as f:
config = json.load(f)
except FileNotFoundError as e:
logging.error("Config not found", extra={"path": e.filename})
raise SystemExit(1)
except json.JSONDecodeError as e:
logging.error("Invalid JSON", extra={
"line": e.lineno,
"column": e.colno,
"msg": e.msg
})
raise
try-except での細分化と structured logging が基本。
GoのError型
if err != nil {
return fmt.Errorf("failed to load config from %s: %w", path, err)
}
// エラーラッピングで context を保持
// %w で原因も含める(Go 1.13+)
Goは明示的なエラーチェックを要求し、panicは避ける文化。
エラーハンドリングのアンチパターン
避けるべきパターン:
| アンチパターン | 問題 | 改善 |
|---|---|---|
エラーを無視 try: ... except: pass |
何が起きたか分からない | 最低限 log.warning() する |
汎用例外を投げる raise Exception("...") |
キャッチできない | 具体的な例外クラスを定義 |
| ログに stack trace だけ出す | 文脈がない | タイムスタンプ、user_id、resource_id も含める |
| エラーメッセージの詳細度が一律 | 本番ユーザーに内部情報が見える | user向け/developer向け で分ける |
| エラーで exit(1) しただけ | 原因が分からないまま停止 | 詳細ログ出力後に終了 |
エラーハンドリングのベストプラクティス
3層のエラーハンドリング
Layer 1: エラーをキャッチして記録
logger.exception(...) # スタックトレース付きログ
Layer 2: ユーザーに伝わる形に変換
return {"error": "Order could not be processed"}
HTTP 400 Bad Request
Layer 3: システムは続行 または 停止判定
if critical: sys.exit(1)
else: continue
Python での実装
import logging
import traceback
from functools import wraps
logger = logging.getLogger(__name__)
class ApplicationError(Exception):
"""アプリケーション固有エラー"""
def __init__(self, message, code=None, http_status=500):
self.message = message
self.code = code or self.__class__.__name__
self.http_status = http_status
super().__init__(self.message)
class ValidationError(ApplicationError):
def __init__(self, message):
super().__init__(message, code="VALIDATION_ERROR", http_status=400)
class DatabaseError(ApplicationError):
def __init__(self, message):
super().__init__(message, code="DB_ERROR", http_status=500)
def handle_errors(func):
"""デコレータ: 全エラーをキャッチしてログ"""
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except ApplicationError as e:
# アプリエラー: 既知のエラー
logger.warning(
f"Application error in {func.__name__}",
extra={
"error_code": e.code,
"http_status": e.http_status,
"message": e.message
}
)
raise
except Exception as e:
# 予期しないエラー
logger.error(
f"Unexpected error in {func.__name__}",
extra={
"error_type": type(e).__name__,
"traceback": traceback.format_exc()
},
exc_info=True
)
raise ApplicationError("Internal server error", http_status=500)
return wrapper
@handle_errors
def process_order(order_id, items):
"""エラーハンドリング付き注文処理"""
# バリデーション
if not items:
raise ValidationError("Items cannot be empty")
try:
# DB操作
db.save_order(order_id, items)
except ConnectionError as e:
raise DatabaseError(f"Could not save order: {str(e)}")
return {"order_id": order_id, "status": "created"}
Rust での結果型(Result)
use std::io;
// Result型: Ok(T) または Err(E)
fn read_config(path: &str) -> Result<Config, io::Error> {
let content = std::fs::read_to_string(path)? // ? で自動伝播
let config: Config = toml::from_str(&content)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
Ok(config)
}
// 呼び出し側
match read_config("app.toml") {
Ok(config) => println!("Config loaded: {:?}", config),
Err(e) => eprintln!("Failed to load config: {}", e),
}
// または ? で伝播
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = read_config("app.toml")? // エラーならここで return
println!("{:?}", config);
Ok(())
}
エラー調査の実践的フロー
5ステップ調査方法
Step 1: エラーメッセージ全体をコピー&読む
Type: KeyError
Message: 'customer_id'
Occurred at: order.py:42
Step 2: Stack traceを遡る(下から上へ)
最下部 = 実際のエラー発生箇所
上層 = そこへいたるコールパス
Step 3: エラー発生時のコンテキストを確認
入力値, 状態変数, リソースの状態
Step 4: 同じエラーを最小コードで再現
必要な入力だけで再現可能なコードを書く
Step 5: 根本原因を特定して修正
修正後、Step 4のテストで検証
よくあるエラーパターン集
| エラー | 原因 | 対処 |
|---|---|---|
| NullPointerException | nullをdereference | null チェック追加 |
| OutOfMemoryError | メモリ枯渇 | メモリリーク調査 |
| TimeoutException | 処理が遅すぎる | タイムアウト値↑ or 処理最適化 |
| ConnectionRefused | サーバーが起動していない | サーバー起動確認 |
| Permission Denied | ファイル権限不足 | chmod 755 |
| Port already in use | 別プロセスがポート使用 | lsof -i :8080 で確認 |
| 404 Not Found | URLが間違っている | URLを確認 |
| 500 Internal Server Error | サーバーロジックエラー | サーバーログ確認 |
デバッグツール活用
Node.js: デバッガー
# Chrome DevTools で デバッグ
node --inspect app.js
# または Node Debugger
node inspect app.js
> c # 続行
> n # 次の行へ
> s # ステップイン
> out # ステップアウト
> watch(expr) # 式を監視
Python: pdb (Python Debugger)
import pdb
def problematic_function(x):
pdb.set_trace() # ここで停止
result = x / 0
return result
# コマンド
# (Pdb) p x # 変数表示
# (Pdb) l # ソース行表示
# (Pdb) n # 次の行へ
# (Pdb) s # ステップイン
# (Pdb) c # 続行
# (Pdb) w # スタックトレース
Go: delve デバッガー
# インストール
go install github.com/go-delve/delve/cmd/dlv@latest
# デバッグ実行
dlv debug main.go
# コマンド
(dlv) break main.main # ブレークポイント設定
(dlv) continue # 続行
(dlv) print x # 変数表示
(dlv) next # 次の行へ
エラーとモニタリングの統合
Sentry によるエラー追跡
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
sentry_sdk.init(
dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
integrations=[FlaskIntegration()],
traces_sample_rate=1.0,
release="1.0.0",
environment="production"
)
@app.route('/order', methods=['POST'])
def create_order():
try:
# ビジネスロジック
return {"status": "created"}
except Exception as e:
# Sentryに自動レポート
sentry_sdk.capture_exception(e)
# ユーザーには汎用エラーを返す
return {"error": "Order creation failed"}, 500
出力例(Sentry Dashboard):
Error: KeyError 'customer_id'
Release: 1.0.0
Environment: production
Affected Users: 47
First Seen: 2 hours ago
Last Seen: 5 minutes ago
Events: 156
まとめ
エラーメッセージは、原因を探すための情報です。種類、場所、理由、文脈に分け、最初のエラーと最後のエラーを見比べると、落ち着いて調査できます。stack trace、exit code、errno、HTTP status、cloud固有のerror codeをそれぞれの層で読み、最小再現で原因を小さくすると、調査はかなり速くなります。
参考文献
公式・標準
- MDN Web Docs: HTTP response status codes
- MDN Web Docs: JavaScript error reference
- Python Documentation: Errors and Exceptions
- Go errors package
- The Rust Programming Language: Error Handling