Node.js ESM の CLI が無言で exit 13 — 循環 import × top-level await デッドロックの診断と修正
テストは通るのにCLI直接起動だけ止まる原因(TLAで待つエントリへ動的import先が循環import)の確認手順・コード無変更の回避策・恒久修正・スモークテスト
約3.7万トークンの節約 (API料金換算で約56円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「Node.js ESM の CLI が無言で exit 13 — 循環 import × top-level await デッドロックの診断と修正」は、開発プロセスカテゴリのAI指示書(MDファイル)です。テストは通るのにCLI直接起動だけ止まる原因(TLAで待つエントリへ動的import先が循環import)の確認手順・コード無変更の回避策・恒久修正・スモークテストこの巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約3.7万トークン(API料金換算で約56円)・93%のトークンを節約できます。
- カテゴリ
- 開発プロセス
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約4万トークン
- この巻物使用時
- 約3,000トークン
- 節約量
- 約3.7万トークン (約56円)
- 更新日
- 2026-10-12
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/node-js-esm-cli-exit-13-import-top-level-await/raw を読み込んで、この指示書どおりに実装して"
中身
Node.js ESM の CLI が何も出さずに exit 13 で止まる — 循環 import × top-level await のデッドロック診断と直し方
症状
node tool.mjs ...を実行すると、何も出力しないまま次の警告で終わる。終了コードは 13Warning: Detected unsettled top-level await at file:///.../tool.mjs:NNN process.exitCode = await main();- テスト(main を import して呼ぶ形)は通る。CLI として直接起動したときだけ止まる。
原因
- エントリの tool.mjs が、末尾で
if (isEntry) process.exitCode = await main();のように top-level await で main を待っている。 - main の中で
await import('./helper.mjs')を動的に読み込む。 - helper.mjs が
import { x } from './tool.mjs'で tool.mjs を静的に読み込んでいる(循環)。 - tool.mjs はまだ評価中(TLA で main を待っている)なので、helper.mjs の評価は tool.mjs の評価完了を待つ。main は helper を待つ。互いに待ち合って、イベントループが空になる → Node が「unsettled TLA」として exit 13 にする。
テストでは tool.mjs を普通に import して評価が完了してから main を呼ぶので、循環しても止まらない。
確認手順(AI にそのまま渡せる)
- 警告に出た行が
await main()のような TLA であることを確認する。 - main の中にある
await import(...)をすべて列挙する。 - それぞれの読み込み先(とその先)から、エントリファイルを静的 import していないかを
grep -n "^import" <file>で調べる。 - 戻ってくる import があれば、それが原因。
すぐ使える回避策(コードを変えずに動かす)
別ファイルから import して main を呼ぶと、エントリ判定が偽になり TLA が走らないので動く。
// run.mjs
import { pathToFileURL } from 'node:url';
const m = await import(pathToFileURL('<tool.mjs の絶対パス>').href);
process.exitCode = await m.main(process.argv.slice(2));
node run.mjs <元と同じ引数>
恒久修正(どちらか)
- A: 循環を断つ。helper が tool.mjs から借りている関数(例: ラベル解決)を、両者が import する小さな共通モジュールへ切り出す。最も素直。
- B: エントリで TLA を使わない。
if (isEntry) main().then(code => { process.exitCode = code; }, err => { console.error(err.message); process.exitCode = 1; });
B は症状を消すだけで、循環そのものは残る。可能なら A。
再発防止
- CLI 経路のスモークテストを 1 本足す:
child_process.spawnSync(process.execPath, ['tool.mjs', '--dry-run'])を実行し、status が 13 でないこと・期待の出力が出ることを確かめる。import して main を呼ぶテストだけでは、この不具合を検出できない。
よくある質問
+「Node.js ESM の CLI が無言で exit 13 — 循環 import × top-level await デッドロックの診断と修正」とは何ですか?
テストは通るのにCLI直接起動だけ止まる原因(TLAで待つエントリへ動的import先が循環import)の確認手順・コード無変更の回避策・恒久修正・スモークテスト
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約4万トークンかかりますが、この巻物を使えば約3,000トークンで済みます。差し引き約3.7万トークン(API料金換算で約56円)・93%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
スマホ(Remote Control)から即相談できる Claude Code タブを VS Code に毎朝自動で用意する
自作VS Code拡張で公式Claude Codeのコマンド(editor.openLast/newConversation/renameSessionTab)を叩き、名前付きタブをN本自動補充。夜間はWM_CLOSE→再起動で毎朝揃える。--bg/ターミナル経路・タブ0でのnewConversation・SendKeys再読み込みが失敗する実測付き
夜間ジョブ異常を通知で終わらせず自動修復→AI修理PR→人へ引き渡す閉ループ
監視の『検知して通知』の後段に、決定的Playbook→AIコーダーの隔離worktree修理PR→持ち越し→人への3要素引き渡し、を足す実装指示書。argvで指示を渡すな等の実測の落とし穴つき
ドキュメント駆動開発プロセス CLAUDE.md — 作るものを固めてから書かせる
「AIが暴走して意図と違うものを作る」を根絶する開発プロセス指示書。UI仕様→機能設計→実装の順をAIに強制し、1ファイルごとに承認ゲートを挟む。受託開発・チーム開発向け。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア