エラーメッセージの読み方

目次

概要

エラーメッセージは、失敗の原因を探すための地図です。怖いものではなく、システムが「どこで、何が、なぜ失敗した可能性があるか」を教えてくれる情報です。

要点

エラーは、種類、場所、原因、再現条件に分けて読みます。全文を読む前に、最初のエラー、最後のエラー、ファイル名、行番号、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
flowchart LR E["error message"] --> T["type"] E --> L["location"] E --> R["reason"] E --> C["context"]

まず見る順番は、だいたい次です。

順番 見るもの
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)

読む順序です。

  1. エラー種別を見る
  2. メッセージを見る
  3. 自分のコードの最初の行を探す
  4. その関数に渡された値を確認する
  5. 直前の変更を確認する

ライブラリ内部の行が多くても、必ずしもそこが原因とは限りません。自分のコードから不正な値を渡していることも多いです。

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)

この形では、下に行くほど低レベル、上に行くほどアプリケーション上の意味に近くなります。

flowchart BT A["No such file or directory"] --> B["failed to read production.json"] B --> C["failed to load application config"] C --> D["server startup failed"]

原因連鎖を見るときは、「最も低レベルの原因」と「ユーザー影響に近い失敗」の両方をメモします。

見るもの 役割
root cause ENOENT 直接直す対象
context load application config 何の処理中か
impact server startup failed 何ができなかったか

Rustの anyhow やGoのwrapped errorのように、低レベルエラーへ文脈を足していく文化があります。読む側は「最後に表示された1行」だけでなく、Caused bysourcewrappedbecause を探します。

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由来のエラーでは、ENOENTEACCESECONNREFUSED のような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

EACCESEPERM は似ています。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 一時的に利用不可

403404 は似て見えますが、原因が違います。権限問題なのか、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の失敗など、層が違うことがあります。

flowchart TD A["HTTP 403"] --> B["認証済みか"] A --> C["認可policy"] A --> D["WAF / gateway"] A --> E["CORS preflight"] A --> F["cloud resource policy"]

networkとTLSのエラー

network errorは、アプリケーションのエラーに見えても、DNS、TCPTLS、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

切り分けは、層を下から見ます。

flowchart TD A["名前解決"] --> B["TCP接続"] B --> C["TLS handshake"] C --> D["HTTP request"] D --> E["application response"]
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 byRUST_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は長くなりがちです。最初に出た errorERR!Caused byfailed 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では logsdescribe をセットで見ます。

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が返ります。AccessDeniedNoSuchBucketNoSuchKeyInvalidAccessKeyId のようなcodeは、HTTP statusより具体的です。

権限エラーでは、credentialが想定通りかを先に確認します。

aws sts get-caller-identity
aws configure list

「権限がない」の前に、「そもそも違うアカウント/roleで実行している」こともよくあります。

原因調査の手順

flowchart TD A["全文を保存"] --> B["最初のエラーを見る"] B --> C["場所を特定"] C --> D["直前の変更を確認"] D --> E["最小再現を作る"] E --> F["仮説を1つずつ潰す"]

調査では、同時に多くを変えないことが重要です。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の直前変更を確認

質問や報告の目的は、詳しく見せることではなく、相手が同じ状態を再現できるようにすることです。ログ全文は長くてもよいですが、本文では重要な行を先に抜き出します。

エラー分類マップ

エラーは、まず種類で分けると調査しやすくなります。

flowchart TD E["error"] --> S["syntax / compile"] E --> R["runtime"] E --> N["network"] E --> P["permission"] E --> C["configuration"] E --> D["data"]
分類 典型例 最初に見るもの
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不一致 入力データ

分類できるだけで、見るべき資料がかなり絞れます。

もう少し実務寄りに分けると、次のようになります。

flowchart TD E["error"] --> A["自分のコード"] E --> B["入力データ"] E --> C["設定・環境"] E --> D["依存ライブラリ"] E --> F["外部サービス"] E --> G["OS / runtime"] E --> H["権限"]
分類 質問
自分のコード 直前の変更で壊したか
入力データ 特定の入力だけで落ちるか
設定・環境 localとCI、本番で差があるか
依存ライブラリ version updateがあったか
外部サービス timeout、rate limit、障害情報はあるか
OS / runtime path、permission、version、resource不足か
権限 誰が、何に、どのactionをしたか

この分類に沿って「同じコードで環境を変える」「同じ環境で入力を変える」のように1軸ずつ動かすと、原因に近づきやすくなります。

最小再現を作る

原因が分からないときは、最小再現を作ります。最小再現とは、問題を起こすために必要な最小のコード、入力、手順です。

flowchart LR Big["大きな問題"] --> Remove["関係ないものを削る"] Remove --> Small["小さな再現"] Small --> Fix["原因を特定"]

作り方です。

  1. エラーメッセージ全文を保存する
  2. 入力を小さくする
  3. 関係ない依存を外す
  4. 1ファイルで再現できるか試す
  5. 成功するケースと失敗するケースを並べる

最小再現は、質問のためだけでなく、自分が原因を理解するためにも強力です。削っている途中で原因が見つかることも多いです。

最小再現では、次を削ります。

削るもの
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 traceexit code、errno、HTTP status、cloud固有のerror codeをそれぞれの層で読み、最小再現で原因を小さくすると、調査はかなり速くなります。

参考文献

公式・標準

解説・補助