エラーメッセージとは - 読み方 / 原因特定 / 設計のベストプラクティス
エラーメッセージはプログラムが問題を検出した際に原因と場所を伝える通知。スタックトレースの読み方 / ユーザー向けメッセージの設計指針を解説
エラーメッセージとは
エラーメッセージ (Error Message) は、プログラムが問題を検出したときに出力する通知である。何が起きたか、どこで起きたか、なぜ起きたかを開発者に伝え、修正の手がかりを提供する。
エラーの種類
エラーは発生するタイミングで大きく 3 つに分かれる。
| 種類 | 発生タイミング | 例 |
|---|---|---|
| 構文エラー | コードの解析時 | 括弧の閉じ忘れ、引用符の閉じ忘れ |
| 実行時エラー | プログラムの実行中 | null 参照、ゼロ除算、型の不一致 |
| 論理エラー | 実行は成功するが結果が間違い | 条件式の誤り、計算ロジックのバグ |
構文エラーと実行時エラーはエラーメッセージが出るが、論理エラーはメッセージが出ないため発見が難しい。論理エラーだけは処理系が気づけないので、期待値を書いたテストか、出力の目視でしか捕まらない。
言語ごとに「どこまでが構文エラーか」も違う。JavaScript には自動セミコロン挿入 (ASI) の規則があり、行末のセミコロンを省いても多くの場合は解析器が補うため、省略そのものは構文エラーにならない。代わりに、改行の入れ方だけで意図しない位置に文の区切りが入る形の事故が起きる。この言語では何が構文エラーになり、何が黙って通るのかを知っているかどうかが、メッセージの読み取り速度を決める。
エラーメッセージの読み方
エラーメッセージには通常、以下の情報が含まれる。
TypeError: Cannot read properties of undefined (reading 'name')
at getUser (src/user.ts:15:20)
at main (src/index.ts:8:10)
1 行目がエラーの種類と内容。TypeError が種類、Cannot read properties of undefined が内容である。2 行目以降がスタックトレースで、エラーが発生した場所を示す。src/user.ts:15:20 は「user.ts の 15 行目 20 文字目」を意味する。
よくあるエラーメッセージ
| メッセージ | 原因 | 対処 |
|---|---|---|
ReferenceError: x is not defined | 未宣言の変数を参照 | 変数名のスペルミスを確認 |
TypeError: x is not a function | 関数でないものを呼び出し | 変数の型を確認 |
SyntaxError: Unexpected token | 構文の誤り | 括弧や引用符の対応を確認 |
RangeError: Maximum call stack | 無限再帰 | 基底条件を確認 |
Cannot find module | モジュールが見つからない | パスとインストール状況を確認 |
エラーメッセージを活用するコツ
要点の 1 行を先に見つける
スタックトレースが長くても、エラーの種類と内容を述べている行は 1 行しかない。まずその行を探す。ただし位置は言語によって違う。JavaScript や Java は先頭に来るが、Python は逆で、Traceback (most recent call last): の後に呼び出しの並びが続き、公式チュートリアルが「エラーメッセージの最後の行が何が起きたかを示す」と説明するとおり、種類と内容は末尾にある。Java で Caused by: が連なる場合も、根本原因は下にある方が近い。
「先頭を読む」と機械的に覚えると、言語が変わった瞬間に呼び出し元のフレームだけを見て悩むことになる。探すべきは位置ではなく、種類名 (TypeError など) と内容が書かれた行である。
ファイル名と行番号を確認する
スタックトレースの中から、自分が書いたコードのファイル名を探す。ライブラリの内部で例外が投げられていても、そのライブラリに渡す値を決めたのは自分のコードなので、原因は自分のフレーム側にあることが多い。node_modules のフレームは「原因の場所」ではなく「どの経路で呼ばれたか」として読み、最も内側にある自分のコードの行を起点にする。
ビルドやバンドルを通したコードでは、行番号が変換後のファイルを指してしまう。ソースマップを有効にしておくと、元のファイルと行へ読み替えられる。
エラーメッセージで検索する
エラーメッセージをそのまま検索エンジンに貼り付けると、同じ問題に遭遇した人の解決策が見つかることが多い。貼るのは種類名と定型の文面までにし、ファイルパス・ホスト名・トークン・利用者のデータが混ざった部分は削る。エラーメッセージは環境の内情をそのまま含むため、貼り付け先が外部サービスであることを忘れやすい。
良いエラーメッセージの設計
自分でエラーメッセージを書く場合は、以下を心がける。
// ❌ 曖昧なメッセージ
throw new Error("エラーが発生しました");
// ✅ 具体的なメッセージ
throw new Error(
`ユーザー ID "${id}" が見つかりません。` +
`有効な ID は正の整数です。受け取った値: ${id}`
);
| 原則 | 説明 |
|---|---|
| 何が起きたか | エラーの種類を明示する |
| どこで起きたか | 関連するデータや文脈を含める |
| どうすればよいか | 修正のヒントを添える |
ただしこの 3 原則は、開発者が読むメッセージの話である。同じ内容を利用者の画面へそのまま出すと、直し方の分からない技術情報を突きつけることになる。宛先で書き分ける。
利用者に見せるエラーメッセージ
Nielsen Norman Group が公開しているエラーメッセージの設計指針は、見せ方・伝え方・手間の削減の 3 系統に整理されている。実装で効くのは次の点である。
| 指針 | 実装上の意味 |
|---|---|
| 発生源の近くに出す | 画面上部にまとめず、対象の入力欄の隣に出す |
| 色だけに頼らない | 赤字や枠線に加えて文言とアイコンを添える (色覚特性への配慮) |
| 利用者を責めない | 落ち度があるかのような言い回しを避け、期待する形式を伝える |
| 入力を保持する | エラー後にフォームを空へ戻さない |
| 早すぎる表示をしない | 入力欄から離れただけで未入力エラーを出さない |
同指針は invalid・illegal・incorrect のような語を、利用者に落ち度があるかのように響く表現として避けるよう述べている。日本語でも「不正な入力です」「エラーが発生しました」だけの文面は同じ問題を抱える。
エラーメッセージとセキュリティ
エラーメッセージは攻撃者にとって偵察の材料になる。OWASP のエラー処理チートシートは、処理されなかった例外がフレームワークやアプリケーションサーバーのバージョンを画面へ出してしまう例と、データベースのエラー文面が設置パスや注入の入口の手がかりを与える例を挙げている。
対処は宛先の分離である。利用者へは一般的な文面と識別子だけを返し、種類・スタックトレース・発行した問い合わせ・内部パスはサーバー側のログにだけ残す。識別子を突き合わせれば、問い合わせ窓口からの連絡と該当ログを結び付けられる。
HTTP API のエラー
HTTP では、状態コードの階級を取り違えないことが出発点になる。RFC 9110 は 4xx を「クライアントが誤ったと思われる」場合、5xx を「サーバー自身が誤った、または要求された処理を行えない」場合と定めている。入力の検証漏れを 500 で返すと、監視する側は自分の障害と利用者側の誤りを区別できなくなる。
同 RFC の 404 の定義には、対象の表現が無い場合だけでなく「存在することを明かす意思がない」場合も含まれる。他人の資源へのアクセスを 403 で返すと存在自体が漏れるため、404 を選ぶ設計の根拠はここにある。
本文の形式は RFC 9457 (Problem Details for HTTP APIs・RFC 7807 を置き換えた) が定めており、application/problem+json として type・title・status・detail・instance を返す。
{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"status": 403,
"detail": "Your current balance is 30, but that costs 50."
}
同 RFC は detail を、デバッグ情報を与えるためではなくクライアントが問題を正せるようにするためのものと位置づけ、スタックダンプのような実装の詳細を HTTP 経由で得られる状態にしないよう促している。status を本文にも書く場合、経路上のプロキシが状態コードを書き換えると本文と食い違い得る点も注意点として挙げられている。
try-catch によるエラー処理
エラーが発生しうる処理は try-catch で囲み、適切に処理する。
try {
const data = JSON.parse(rawInput);
processData(data);
} catch (error) {
if (error instanceof SyntaxError) {
console.error("JSON の形式が不正です:", error.message);
} else {
throw error; // 想定外のエラーは再送出
}
}
エラーを握りつぶす空の catch ブロックは、問題の発見を遅らせるため避ける。
途中で捕まえて文脈を足したいときは、元のエラーを捨てずに繋ぐ。JavaScript の Error は第 2 引数に cause を取り、より具体的な文面で投げ直しても元のエラーへ辿れる (主要ブラウザでは 2021 年 9 月以降利用できる)。
try {
await saveOrder(order);
} catch (error) {
throw new Error(`注文 ${order.id} の保存に失敗しました`, { cause: error });
}
文面を差し替えるだけの再送出は、原因側の行番号と種類を失わせる。読み手が最終的に必要なのは、どの層で何が起きたかの連なりである。層をまたぐたびに文脈を足し、原因は残す。
この記事は役に立ちましたか?
関連用語
デバッグ
プログラムの不具合を発見し、原因を特定して修正する一連の作業
ロギング
アプリケーションの動作を記録し、デバッグ / 監視 / 監査に活用する仕組み
構造化ログとは - JSON ログの設計と検索性
構造化ログとは、ログを JSON など機械可読な形式で出力し、フィールド単位の検索 / 集計を可能にするロギング手法。非構造化ログとの違い、含めるべきフィールド、失敗パターンを解説
デッドレターキュー
処理に失敗したメッセージを退避させ、後から調査 / 再処理するためのキュー
Pub/Sub
発行者と購読者が直接通信せず、メッセージブローカーを介して非同期にメッセージを交換するパターン
SNS と SQS
AWS のメッセージングサービスで、SNS がパブリッシュ/サブスクライブ、SQS がメッセージキューを提供する