マガジン

スタックトレースの読み方:自分のコードの行を見つけてAIに渡す範囲を決める

AIを使いこなす

Claudeで執筆し、GPTで自動校閲・修正しています。

長い紙帯から関連する範囲を選び、クリップで印を付ける手元。
記事の内容をもとに制作したAI生成イメージです。実際の製品画面や検証結果の写真ではありません。

結論から言うと、まず見る場所は2つです。1つ目はエラーの種類とメッセージ(例では TypeError: ... の行)、2つ目は at で始まる行のうち、自分で管理するソースファイルが出てくる最初の行です。node_modules/ は依存ライブラリ、node:internal/ はNode.js内部の処理を見分ける手掛かりです。ただし、最初の自分のコードの行が修正箇所とは限らず、部品側にも原因がありえます。 AIに渡す範囲は「エラーのメッセージ、自分のコードの行、その行の関数本体、関係する呼び出し元と部品側の行」を出発点にします。部品側の長い行は、原因の判断に関係しないと確認できた部分を省きます。逆に、自分のコードの行が1つも見つからないときや、どこを省けるか迷うときは、秘密情報を伏せたうえでトレース全体を渡します。 以下の例は、読み方を示すための説明用の例です。実際に実行した出力ではなく、パスやコードも架空のものです。

この記事の目次
  1. 1. スタックトレースは「呼び出しの履歴」
  2. 2. 行の印付け例
  3. 3. AIに渡す抜粋範囲の判断表
  4. 4. そのまま使える依頼文
  5. 5. 貼る前と、直した後の確認
  6. 6. 迷ったときの最小ルール

1. スタックトレースは「呼び出しの履歴」

Node.jsの error.stack には、エラーが作られた時点の呼び出しの履歴が文字列で入っています。Node.js公式ドキュメントによると、この履歴はV8の仕組みに基づき、同期処理の開始地点まで、または Error.stackTraceLimit で決まるフレーム数のうち小さいほうまでしか含まれません。

つまり次の2点を押さえておくと読み間違いにくくなります。

  • 上の行ほどエラーが作られた場所に近く、下の行ほど呼び出し元に近い。エラー生成位置と根本原因は一致するとは限らない。
  • 長いトレースでも途中で切れていることがある。非同期処理では呼び出し元まで載らない場合もある。

なお、stack の書式はJavaScriptエンジンごとに違い、標準化されていないプロパティだとMDNは説明しています(Error: stack - MDN)。ここで示すのはNode.js(V8)形式の at 関数名 (ファイル:行:列) です。ブラウザのSafariやFirefoxでは見た目が違います。

2. 行の印付け例

説明用の例として、ECサイトのカート計算で出たエラーを想定します。各行の頭に印を付けました。

  • 【自】自分のコード
  • 【部】node_modules 内の部品(ライブラリ)
  • 【内】Node.jsの内部処理(この例では node:internal/)
  • 【言】言語組み込み(この例では Array.map)

<anonymous> は匿名関数などにも現れるため、この表記だけで言語組み込みとは判断しません。

text
/Users/demo/shop-app/src/price.js:12
  return item.price * item.qty;
              ^

TypeError: Cannot read properties of undefined (reading 'price')
【自】 at calcLine (/Users/demo/shop-app/src/price.js:12:15)
【自】 at /Users/demo/shop-app/src/cart.js:8:30
【言】 at Array.map (<anonymous>)
【自】 at sumCart (/Users/demo/shop-app/src/cart.js:8:22)
【自】 at handleCheckout (/Users/demo/shop-app/src/routes/checkout.js:21:15)
【部】 at Layer.handle [as handle_request] (/Users/demo/shop-app/node_modules/express/lib/router/layer.js:95:5)
【部】 at next (/Users/demo/shop-app/node_modules/express/lib/router/route.js:149:13)
【内】 at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

(印は説明のために付けたもので、実際の出力には出ません)

読む順番

  1. メッセージ:undefined の price を読もうとして失敗した、とあります。つまり item が undefined です。
  2. 最初の【自】の行:price.js の12行目が、エラーが起きた場所です。
  3. 次の【自】の行:calcLine の呼び出し元は cart.js の8行目です。引数をどう作って渡したか、ここも見ます。
  4. 【部】【内】の行は呼び出し経路を示します。ただし、この表示だけで部品側に原因がないとは判断できません。

この例では、price.js の12行目を直すより、cart.js で undefined の要素が混ざる理由を調べるほうが本筋かもしれません。AIには、この2か所をセットで見せる必要があります。

3. AIに渡す抜粋範囲の判断表

トレースの形ごとに、渡す範囲を決める表です。編集上の目安なので、状況に応じて足し引きしてください。【部】【内】も原因候補に関係する場合や、判断に迷う場合は残します。共有前に第5節の確認を行ってください。

  • 最初の at が【自】、渡すもの:メッセージ、【自】の行すべて、各行の関数本体、入力データの形、省いてよいもの:【部】【内】の残り行(省略した旨は書く)
  • 最初の at が【部】で、すぐ下に【自】、渡すもの:メッセージ、最初の【部】行、その下の【自】の行と呼び出し部分のコード、ライブラリ名とバージョン、省いてよいもの:深い位置の【部】【内】
  • 【自】が1行もない(【部】【内】だけ)、渡すもの:トレース全体、実行コマンド、起動直後に動くファイル(エントリポイント)、省いてよいもの:なし。削らない
  • トレースが短い・途中で切れている、渡すもの:全体、await や setTimeout を使っている箇所のコード、省いてよいもの:なし
  • 原因情報が付いている(cause あり)、渡すもの:外側のメッセージとトレース、cause の内容(エラーならそのメッセージとトレースも)、省いてよいもの:重複する【部】行(省略した旨は書く)
  • dist/ など変換後のパスが出る、渡すもの:変換前の該当ファイルとの対応、または後述のソースマップ設定後の出力、省いてよいもの:変換後コードの全文
  • ログ全体が数百行、渡すもの:問題の操作に対応するエラーの塊と、関連する前後のログ、省いてよいもの:無関係と確認できた警告や重複出力(関連が不明なら残す)

cause(原因エラー)がある場合

new Error("...", { cause: err }) では、指定した err が cause に入ります(Error: cause - MDN)。元のエラーを保持して文脈を加えるために使えますが、cause は自動で付くとは限らず、エラー以外の値も指定できます。表示形式は環境により違うため、外側のメッセージとトレースに加え、cause の内容も確認して一緒に渡してください。

TypeScriptなどを変換している場合

変換後のファイルが出力に出るなら、ソースマップが生成され、Node.jsから参照できることを確認して、--enable-source-maps を試します(Node.js CLIドキュメント)。元のソース位置の表示は保証されず、Error.prepareStackTrace の上書きが妨げになる場合もあります。また公式は、この設定を有効にして Error.stack にアクセスすると遅延が生じる場合がある、と注記しています。頻繁にアクセスする運用では性能への影響も確認してください。

トレースが短すぎるとき

フレーム数の上限は --stack-trace-limit=50 のように指定して増やせます(同じCLIドキュメント)。【自】の行が見つからず、上限で切れているように見えるときだけ試してください。

4. そのまま使える依頼文

下の文面の 【 】 を埋めて使います。入力例は、前節の説明用の例に合わせています。

text
Node.jsのアプリで次のエラーが出ます。原因の候補を挙げ、最小の修正案を出してください。

■ 環境
Node.js【バージョン】、express【バージョン】、実行コマンド:node src/server.js

■ エラー(部品側の行は省略しています)
TypeError: Cannot read properties of undefined (reading 'price')
  at calcLine (src/price.js:12:15)
  at src/cart.js:8:30
  at sumCart (src/cart.js:8:22)
  at handleCheckout (src/routes/checkout.js:21:15)
  ...(express内部が数行続く)

■ 該当コード
【price.js の calcLine 関数】
【cart.js の sumCart 関数】

■ 入力データの形(個人情報は架空の値に置換済み)
cart.items = [{ price: 100, qty: 2 }, undefined]

■ 依頼
1. 原因の候補を可能性の高い順に2〜3個。根拠は上のコードの行番号で示してください。
2. 修正は、calcLine側とsumCart側のどちらで行うべきか理由つきで。
3. 修正後に確認するテスト入力を1つ。
4. 渡した情報だけでは判断できない点があれば、推測せず質問してください。

「推測せず質問してください」と依頼しても、回答が推測を含まない保証はありません。根拠に挙げられたコードを確認してください。

5. 貼る前と、直した後の確認

貼る前

  • パスにユーザー名や社内のディレクトリ名が入っていないか。入っていれば /Users/demo/ のように置き換えます。
  • エラーメッセージにトークン、接続文字列、メールアドレス、URLのクエリが含まれていないか。含まれていれば伏せ字にします。
  • 省略した部分は「省略した」と明記します。黙って削ると、AIは「元からその構造だった」と読んでしまいます。

AIの回答を受け取った後

人が確認するのは次の3点です。

  1. AIが挙げた行番号が、手元のファイルの実際の行と一致しているか(違えば、古いコードや想像で答えている可能性があります)。
  2. 修正を入れて、元のエラーを起こした操作をもう一度実行し、同じエラーが消えたか。
  3. 別の入力(空の配列や1件だけのデータなど)でも壊れていないか。

エラーが消えても、undefined を単に無視する修正だと、原因である「なぜ混ざったか」が残ります。AIの案が値を握りつぶすだけの内容なら、前節の依頼の2番を使って、どの層で直すべきかを聞き直してください。

6. 迷ったときの最小ルール

  • 自分で管理するソースファイルの行が見つかれば、その行と、呼び出し元に当たる【自】の行を一緒に渡す。
  • 見つからなければ秘密情報を伏せ、トレース全体を渡す。
  • 部品側の行を省くときは「省略」と書く。

この3つを出発点にして、情報が足りなければ抜粋範囲を広げます。

出典・参考資料