Node.jsのエラー解決手順|メッセージとスタックトレースの読み方

woman in an office looking through documents on her lap
Photo by Mizuno K on Pexels.com

Node.jsでエラーが出たら、すぐにコードを書き換える前に、エラー名、メッセージ、発生箇所の三つを確認します。
表示された情報を順に読めば、調べる範囲を絞り込みやすくなります。

この記事では、エラーの種類を見分け、スタックトレースから自分のコードを探し、値や処理の流れを確認する手順を解説します。
Node.jsそのものの役割が曖昧な場合は、先にNode.jsとブラウザ向けライブラリの役割の違いを確認すると、実行環境を整理できます。

調査を始める前に残す情報

エラーを再現した直後の情報を残しておくと、修正前後を比較できます。
少なくとも次の項目をメモします。

  • 実行したコマンド
  • 省略していないエラーメッセージとスタックトレース
  • エラーが出る直前に変更したコードや設定
  • メッセージに現れるモジュール名と、そのバージョン

まず同じ操作でエラーが再現するかを確認します。
再現しない場合は、入力値や実行順序など、発生したときだけ違っていた条件を調べます。

エラーメッセージとスタックトレースの違い

スタックトレースとは、エラーが発生した場所と、そこへ至るまでに呼び出された関数を並べた記録です。
一般的な表示では、先頭行にエラー名とメッセージがあり、その後にファイル名、行番号、列番号を含む行が続きます。

TypeError: Cannot read properties of undefined (reading 'title')
    at renderTitle (/project/controllers/book.js:22:11)
    at getBookDetail (/project/controllers/book.js:10:3)
  • TypeErrorはエラーの種類です。
  • Cannot read properties of undefinedは、失敗した操作を説明するメッセージです。
  • book.js:22:11は、ファイルの22行目、11列目を示します。
  • 次の行は、renderTitleを呼び出したgetBookDetailの位置を示します。

この例では、renderTitleの22行目で、値がundefinedのままtitleを読もうとしています。
ただし、22行目だけを直せばよいとは限りません。
値を渡した呼び出し元に原因がある可能性もあるため、次のフレームまで確認します。

エラーの種類を手掛かりにする

エラー名は原因そのものではなく、調査の入口です。
名前から確認対象を絞り、メッセージと該当行で判断します。

Node.jsで見かける主なエラー名と確認箇所
エラー名 意味 最初に確認する箇所
SyntaxError JavaScriptの構文を解釈できない状態 括弧、引用符、カンマなど、表示された行の前後
ReferenceError 定義されていない変数や関数を参照した状態 名前のつづり、宣言位置、参照できる範囲
TypeError その値では実行できない操作をした状態 undefinednullを含む対象の実際の値
RangeError 関数が受け付ける範囲から外れた値を渡した状態 引数の値と、許容される範囲
Error アプリケーションやモジュールが通知した一般的な例外 メッセージと、例外を投げた処理の前提条件

スタックトレースを読む順序

  1. 先頭行を読む
    エラー名とメッセージから、失敗した操作を確認します。
  2. 自分のプロジェクトに属する最初のフレームを探す
    ファイル名、行番号、列番号を開き、周辺の処理を読みます。
  3. 一つ前の値を確認する
    関数の引数、戻り値、オブジェクトの中身をconsole.log()などで確認します。
  4. 呼び出し元へ戻る
    該当行の値が想定外なら、その値を渡した関数までさかのぼります。
  5. 依存モジュールとの境界を確認する
    node_modules内のフレームがあっても、直ちにモジュール側の不具合とは判断できません。自分のコードが渡した引数や設定も確認します。

よくあるエラーの切り分け方

Cannot read properties of undefined

このメッセージは、対象がundefinedの状態でプロパティを読もうとしたときに現れます。
非同期処理の結果を受け取る前に参照した場合や、期待したデータが見つからなかった場合など、対象の値が想定と違う箇所を探します。

最初に、アクセス直前の値を確認します。

console.log(book);
console.log(book?.title);

bookが存在しない状態を仕様として許容するなら、条件分岐やオプショナルチェイニングで扱えます。
存在するはずの値なら、アクセスを回避するだけで終えず、どこでundefinedになったかを呼び出し元まで調べます。

モジュールが見つからない

require()importに関するエラーでは、指定したパスと依存関係を分けて確認します。

  • ローカルファイルなら、相対パスとファイル名が実際の配置に合っているかを確認する
  • 外部モジュールなら、package.jsonに必要な依存関係があるかを確認する
  • 依存関係を入れ直す前に、実行しているプロジェクトと作業ディレクトリが正しいかを確認する

Node.jsとパッケージ管理ツールの関係は、Node.js、npm、Yarnなどの違いと使い分けで整理しています。

Promiseの失敗を処理していない

Promiseの失敗に対する処理がないと、実行環境に応じたエラーや警告が表示されます。
表示名や終了時の動作はNode.jsのバージョンや設定で異なるため、古い記事にあるUnhandledPromiseRejectionWarningという文字列だけを探すのではなく、実際に表示されたメッセージを確認します。

async関数内でawaitする処理は、失敗時の扱いを決めておきます。

try {
  await fetchData();
} catch (error) {
  console.error(error);
}

catchでは、記録するだけで処理を続けてよいのか、呼び出し元へ失敗を伝えるのかも判断します。
エラーを表示して終わりにすると、後続処理が成功したように見える場合があるためです。

値の変化をデバッガで確認する

console.log()で原因を絞れない場合は、処理を止めて値を確認します。
Node.jsのコマンドラインデバッガは、次のように起動できます。

node inspect app.js

コードにdebuggerを置くと、その位置を停止地点として利用できます。
VS Codeのブレークポイントを使う場合も、確認する対象は同じです。
条件分岐の直前、関数へ値を渡す直前、非同期処理の結果を受け取った直後を順に確認します。

プロセス警告の発生箇所を追うときは、--trace-warningsを付けると警告のスタックトレースを表示できます。
非推奨機能の警告だけに限定されたオプションではありません。

検索しても解決できない場合

検索結果が当てはまらないときは、情報を増やす前に再現条件を小さくします。

  • エラーメッセージを英語のまま検索する
  • モジュール名とバージョンを検索語に加える
  • GitHub Issues、Stack Overflow、Qiitaなどで同じメッセージを探す
  • 関係する処理だけを残した小さなコードで再現する
  • 変更前後の入力値、設定、実行コマンドを比較する

小さなコードでも再現するなら、問題の範囲はそのコードと実行環境に絞れます。
再現しないなら、元のプロジェクトにある設定、依存関係、処理順序を一つずつ戻して違いを探します。

調査のチェックリスト

  • エラー名とメッセージを省略せずに読んだか
  • 自分のコードに属する最初のファイル名と行番号を開いたか
  • 該当行で使っている値を実行時に確認したか
  • 呼び出し元から渡された値までさかのぼったか
  • 自分のコードと依存モジュールの境界を切り分けたか
  • 必要なら最小構成で再現したか

Node.jsのエラー調査では、メッセージを読むことと、実行時の値を確かめることを分けて進めます。
推測だけで修正を重ねず、スタックトレースが示す場所から一つずつ確認すれば、原因に近づきやすくなります。

投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)