マキモノ
開発プロセス無料✅ 公式検証済みv1.0.0 / 更新

Node.js ESM の CLI が無言で exit 13 — 循環 import × top-level await デッドロックの診断と修正

テストは通るのにCLI直接起動だけ止まる原因(TLAで待つエントリへ動的import先が循環import)の確認手順・コード無変更の回避策・恒久修正・スモークテスト

出品者: cr@orgiast.jp📖 読込 約966トークン (約1円)💰 コスパ 38倍
トークン節約メーター93%節約
ゼロからAIに作らせた場合約4万トークン
このMDを読ませた場合約3,000トークン

約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 のターミナルに貼るだけです。

⬇ .md をダウンロード
claude "https://makimono-md.vercel.app/api/v1/files/node-js-esm-cli-exit-13-import-top-level-await/raw を読み込んで、この指示書どおりに実装して"
claude-codecursorcodex-cliライセンス: 商用利用可 (再販不可)

中身

Node.js ESM の CLI が何も出さずに exit 13 で止まる — 循環 import × top-level await のデッドロック診断と直し方

症状

  • node tool.mjs ... を実行すると、何も出力しないまま次の警告で終わる。終了コードは 13 Warning: Detected unsettled top-level await at file:///.../tool.mjs:NNN process.exitCode = await main();
  • テスト(main を import して呼ぶ形)は通る。CLI として直接起動したときだけ止まる。

原因

  1. エントリの tool.mjs が、末尾で if (isEntry) process.exitCode = await main(); のように top-level await で main を待っている。
  2. main の中で await import('./helper.mjs') を動的に読み込む。
  3. helper.mjs が import { x } from './tool.mjs' で tool.mjs を静的に読み込んでいる(循環)。
  4. tool.mjs はまだ評価中(TLA で main を待っている)なので、helper.mjs の評価は tool.mjs の評価完了を待つ。main は helper を待つ。互いに待ち合って、イベントループが空になる → Node が「unsettled TLA」として exit 13 にする。

テストでは tool.mjs を普通に import して評価が完了してから main を呼ぶので、循環しても止まらない。

確認手順(AI にそのまま渡せる)

  1. 警告に出た行が await main() のような TLA であることを確認する。
  2. main の中にある await import(...) をすべて列挙する。
  3. それぞれの読み込み先(とその先)から、エントリファイルを静的 import していないかを grep -n "^import" <file> で調べる。
  4. 戻ってくる 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で頼むだけで、仕事が完成」。

AI代行堂を見る →

関連する巻物

この巻物、誰かのトークンも救えます

𝕏 で節約レシートをシェア