Error [ERR_REQUIRE_ESM]

require() of ES Module not supported の直し方(Node.js の ERR_REQUIRE_ESM)

FIX SUMMARY verified
Applies when
node:18-alpinerequire() in CommonJS

Verified: reproduced in node:18-alpine, then the ERR_REQUIRE_ESM signature was gone after the fix (exit 0).

CommonJS(.cjsrequire() を使うコード)から ESM 専用モジュールを読み込もうとすると、次のエラーで落ちます。

Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported.
Instead change the require of ... to a dynamic import() which is available in all CommonJS modules.

古い Node(14 系以前)や一部のツール経由では、同じ原因が Must use import to load ES Module という文言で表示されることもあります。

結論から言うと、require() を動的 import() に置き換えるのが最短の直し方です。ただしその前に、あなたの Node のバージョンを確認してください。原因の半分はそこにあります。

まず node -v を確認する

このエラーは Node のバージョンで挙動が変わります。

Node のバージョンrequire('*.mjs') の挙動
18.x/20.0〜20.18/22.0〜22.11ERR_REQUIRE_ESM で落ちる(require(esm) が既定で無効)
20.19.0 以上/22.12.0 以上/23/24落ちない(require(esm) が既定で有効化された)

注意したいのは中間の帯です。「18 系ではないから大丈夫」ではありません。20.0〜20.18 と 22.0〜22.11 も既定では落ちますrequire(esm) の既定有効化は 20.19.0/22.12.0/23.0.0 から)。この差から 「自分のPCでは再現しないのに、CI や本番でだけ出る」 という状況が起こります。手元が 20.19/22.12 以上なら気づけません。まず node -v を実行し、動いている環境のバージョンをそろえて確認してください。

再現(最小構成)

ESM 専用モジュールを 1 つ用意します。

// esm-only.mjs(.mjs は常に ESM として扱われる)
export const greeting = 'hello from an ES module';

これを CommonJS から require() すると落ちます。

// index.cjs
const mod = require('./esm-only.mjs');
console.log(mod.greeting);

Node 18 で実行すると ERR_REQUIRE_ESM が出て、終了コードは 1 になります。

解決:require() を動的 import() に置き換える

ファイルを CommonJS のまま、require() だけを動的 import() に変えます。

// index.cjs(CJS のまま。require() を import() に置き換えただけ)
(async () => {
  const mod = await import('./esm-only.mjs');
  console.log(mod.greeting);
})();

これで Node 18 でも正常に動きます(終了コード 0)。import()require() と違い、この記事が扱う Node 18 以降なら、どの版の CommonJS からでも使えます。

仕組み:Node 18 では require で ESM を読めない

Node 18 では、CommonJS の require() から ESM を直接読み込む機能が既定で有効ではありません。だから同期の require() で ESM を読もうとすると拒否されます。

新しい Node(20.19/22.12 以上)は require(esm) が既定有効になり、トップレベル await を含まない同期的な ESM なら require() で読めます。逆にトップレベル await を含む ESM は評価が非同期になるため、同期の require() では待てず ERR_REQUIRE_ASYNC_MODULE になります(詳細は ERR_REQUIRE_ASYNC_MODULE)。

動的 import() は Promise を返す非同期の読み込みなので、版やトップレベル await の有無に関わらず ESM を読み込めます。CommonJS から確実に読むなら import() が無難です。

それでも直したいときの選択肢

  • Node を上げて直すなら 20.19.0 以上にする。require(esm) が有効になり、同期 require() でも ESM を読めるようになります。ただし読み込む ESM がトップレベル await を含む場合は、上げても require() では読めませんERR_REQUIRE_ASYNC_MODULE になります)。その場合は動的 import() を使ってください。
  • プロジェクト全体を ESM にするなら package.json"type": "module" を足し、require()import 文に置き換える。
  • 依存パッケージが ESM 専用になったことが原因なら、CommonJS 対応の 1 つ前のメジャーバージョンに下げるという手もあります(例: ESM 専用化されたパッケージの直前の版)。ただしこれは一時しのぎで、いずれ移行が必要です。

切り分け(うまくいかないとき)

  • 自分のコードに require() を書いていないのに出るrequire() した先(依存パッケージ)が ESM 専用に変わった可能性が高い。読み込み経路を動的 import() に変えるか、その依存の CommonJS 対応版に下げる。
  • Node を上げたら今度は別のエラーになったERR_REQUIRE_ASYNC_MODULE なら、読み込む ESM がトップレベル await を含んでいる。対処は require() of an ES Module with top-level await(ERR_REQUIRE_ASYNC_MODULE) を参照。
  • import 文に変えたら Cannot use import statement outside a module が出た:そのファイルは CommonJS 扱いのまま。require() のまま動的 import() にするか、package.json"type": "module" を足す。

検証(machine-verified)

この修正は node:18-alpine のバージョン固定コンテナ内で再現し、修正後に ERR_REQUIRE_ESM のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs node/err-require-esm
● reproduce ERR_REQUIRE_ESM present ✓
● apply fix exit 0
● re-run ERR_REQUIRE_ESM gone ✓
PASS verified · node:18-alpine · signature gone