問題
@inquirer/prompts を使った CLI ツールで、ユーザーが Ctrl+C でプロンプトを中断すると、以下のような例外のスタックトレースが表示されてしまう。これはユーザー体験として良くない。
Terminal
ExitPromptError: User force closed the prompt with 0 null
at /path/to/node_modules/@inquirer/core/dist/esm/index.js:123:45
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
...原因
@inquirer/prompts では、ユーザーが Ctrl+C を押すとプロンプトの Promise が reject される。この際に ExitPromptError という名前のエラーがスローされる。
これは意図的な設計で、アプリケーションがクリーンアップ処理を行えるようにするためのもの。しかし、適切にハンドリングしないとスタックトレースがそのまま表示されてしまう。
解決策
グローバルな uncaughtException ハンドラを設定して、ExitPromptError の場合はユーザーフレンドリーなメッセージを表示する。
ポイント: instanceof は使わない
ExitPromptError クラスを直接インポートして instanceof でチェックする方法は、パッケージの構造上うまく動作しないケースがある。
代わりに error.name === 'ExitPromptError' でチェックするのが推奨されている。
実装例
/**
* プロンプトがキャンセルされたエラーかどうかを判定する
*/
function isPromptCancelled(error: unknown): boolean {
return error instanceof Error && error.name === 'ExitPromptError';
}
// グローバルエラーハンドラ
process.on('uncaughtException', (error) => {
if (isPromptCancelled(error)) {
console.log('\nCancelled.');
process.exit(0);
}
throw error;
});これだけで、CLI のどこで Ctrl+C が押されても適切に処理される。
結果
修正前:
Terminal
? Enter title › ^C
ExitPromptError: User force closed the prompt with 0 null
at /path/to/node_modules/@inquirer/core/dist/esm/index.js:123:45
...修正後:
Terminal
? Enter title › ^C
Cancelled.