Error の cause オプションによってエラーの再 throw 時にスタックトレースが失われるのを防ぐ
2025-11-24
6 days ago
開発環境
- Node.js 18+
前提
JavaScript の Error コンストラクタには cause オプションがあり、再スロー(rethrow)する際に元のエラー情報を保持したまま、新しいエラーとして扱うことができます。
従来は throw new Error("xxx: " + error.message) のようにラップするとスタックトレースが失われる問題がありましたが、cause を利用することでスタックトレースを安全に伝搬できます。
本題
cause オプションとは
Error コンストラクタの第 2 引数として cause を渡すと「どのエラーに起因しているか」を表現できます。
try {
doSomething();
} catch (err) {
throw new Error("データ取得時に失敗しました", { cause: err });
}これにより、ツールやランタイムによっては以下のように、スタックトレースがネストされた形で表示されます。
Error: データ取得時に失敗しました
at fetchData …
Caused by: TypeError: fetch is not defined
at doSomething …従来のエラー再スローは元のトレースを失うため、原因調査に時間がかかる問題がありましたが、cause はこれを解消します。
ネストされた例
function parseJson(str) {
try {
return JSON.parse(str);
} catch (err) {
throw new Error("JSON パースに失敗しました", { cause: err });
}
}
function loadConfig() {
try {
const data = readFileSync("./config.json", "utf8");
return parseJson(data);
} catch (err) {
throw new Error("設定ファイルの読み込みに失敗しました", { cause: err });
}
}
loadConfig();最上位ではただの「設定読み込み失敗」だが、cause により
- JSON のパースエラー
- ファイル読み込みエラー
など、発生源まで遡って全てのスタックトレースを保持できます。
エラー分類としての cause 活用
cause は単にエラーの連鎖を作るだけでなく、任意の値を渡すことでエラーを分類する用途にも使えます
例:HTTP ステータスコードでエラーを分類
try {
const res = await fetch("/api/data");
if (!res.ok) {
throw new Error("API エラー", { cause: res.status });
}
} catch (err) {
if (err.cause === 404) {
console.error("データが見つかりませんでした");
} else {
console.error("予期せぬエラーが発生しました");
}
}⭕️ cause の利用が適切なケース
- エラーをラップするが、元のスタックを必ず保持したい
- ビジネスロジック層から詳細を引き剥がしつつ、デバッグ情報は残したい
- API のレスポンスコードや独自エラーコードで分岐したい
- try-catch のネストが深いアプリケーション構造(Next.js API Routes、Cloudflare Workers、バックエンドなど)
❌ 避けたほうがよいケース
- 単純なエラー出力のみで、階層化が不要な場合
- エラー発生場所が深すぎて cause のネストが逆に読みづらくなる場合
さいごに
Error の cause オプションは、従来のエラーラップによるスタックトレース紛失を防ぎ、エラーの原因追跡を大幅に改善する機能です。 また、{ cause: 404 }のように任意データを持たせることでエラー分類にも柔軟に利用できます。 例外処理の品質向上のために、JavaScript / TypeScript プロジェクトでは積極的に活用すべき仕組でしょう。