Node.jsのERR_UNSUPPORTED_DIR_IMPORTの直し方
Node.js で Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import … is not supported resolving ES modules と出て止まるのは、ES モジュールの import にファイルではなくフォルダーを指定したときです。自分のプロジェクトのフォルダーなら、import './lib' を import './lib/index.js' と書き換えるとエラーは消えます。npm のパッケージの中のフォルダーなら、パッケージの exports で入口を決める方法もあります。
require('./lib') で書いていた CommonJS のコードを、そのまま import に書き換えると、このエラーになります。私は検証環境で ./lib のフォルダーを import してエラーを起こし、出たメッセージと、書き換えたあとに消えることを確かめました。
検証環境は Ubuntu 24.04、Node.js 24.22.1(npm 11.19.1)です。エラーの全文と、直したあとの主な結果は、Node.js 22.23.4 でも確かめました。Windows と macOS での動き、TypeScript のファイル(.ts)を直接実行した場合、webpack や Vite などのツールを通した場合は試していません。
フォルダー名だけで import したときのエラー
検証環境で、"type": "module" を書いた package.json のプロジェクトを作りました。src/lib/index.js には、あいさつの文字列を返す関数 greet を export してあります。
export function greet(name) {
return `hello, ${name}`;
}
フォルダー名の ./lib だけを書いて、src/index.js からこの関数を import します。
import { greet } from './lib';
console.log(greet('neko'));
これを node src/index.js で実行したときの出力です。終了コードは1で、パスは例示用の値に置き換えています。
$ node src/index.js
node:internal/modules/esm/resolve:260
throw new ERR_UNSUPPORTED_DIR_IMPORT(path, basePath, String(resolved));
^
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/to/app/src/lib' is not supported resolving ES modules imported from /path/to/app/src/index.js
at finalizeResolution (node:internal/modules/esm/resolve:260:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:705:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:725:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:763:56)
at #resolve (node:internal/modules/esm/loader:687:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:607:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:276:33)
at ModuleJob.link (node:internal/modules/esm/module_job:381:17) {
code: 'ERR_UNSUPPORTED_DIR_IMPORT',
url: 'file:///path/to/app/src/lib'
}
Node.js v24.22.1
Directory import '…/src/lib' is not supported は、「…/src/lib はフォルダーなので、ES モジュールとしては読み込めない」という意味です。後ろの imported from …/src/index.js が、その import を書いたファイルです。lib の中に index.js があっても、Node.js はそれを選んでくれません。ES モジュールの解決アルゴリズムの説明でも、解決した先がフォルダーなら Unsupported Directory Import のエラーにする、と決められています。
Node.js 22.23.4 でも、Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import … の行は同じでした(最初の行に出る resolve の行番号と、スタックの行番号・関数名は少し違います)。'./lib/' と末尾にスラッシュを付けても、Directory import '/path/to/app/src/lib/' is not supported … と、同じエラーで止まりました。
フォルダーに package.json を置いて "main": "index.js" と書いても、ES モジュールの import './lib' は読めません。src/lib/package.json にこれを書いて試したところ、同じ ERR_UNSUPPORTED_DIR_IMPORT で止まりました。
自分のフォルダーなら index.js まで書く
import の行を、フォルダーの中のファイル名と拡張子まで書いた形にします。
import { greet } from './lib/index.js';
console.log(greet('neko'));
$ node src/index.js
hello, neko
エラーは出ず、関数の戻り値が表示されました。Node.js 22.23.4 でも同じでした。
拡張子の .js まで書くのが大事です。'./lib/index' と .js を省くと、今度は Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/path/to/app/src/lib/index' imported from /path/to/app/src/index.js という別のエラーになりました(パスは例示用の値に置き換えています)。このエラーについては、別の記事「Node.jsのERR_MODULE_NOT_FOUNDの直し方」で扱っています。
import のたびに index.js を書きたくないときは、package.json の "imports" に # で始まる名前を決めておく方法もあります。"imports": { "#lib": "./src/lib/index.js" } と書き、import { greet } from '#lib'; とすると、Node.js 24.22.1 と 22.23.4 のどちらでも hello, neko と表示されました。この設定を足したあとも、await import('./lib') と書いた読み込みはエラーのままでした。'#lib' という名前で書いた行だけが対象になるので、直す行が数か所なら、index.js まで書き足すだけで十分です。
npm パッケージの中のフォルダーを import したとき
パッケージの中のフォルダーを import 'パッケージ名/フォルダー名' の形で読んだときも、同じエラーになります。検証用に、exports を書いていない neko-utils というパッケージを作りました。format/index.js に、文字列を大文字にする関数 shout が1つあります。これをローカルのフォルダーから npm install でプロジェクトに入れ、次のように読みました。
import { shout } from 'neko-utils/format';
console.log(shout('neko'));
次のように出ました(パスは例示用の値に置き換え、Did you mean の行より後ろは省いています)。
$ node src/index-pkg.js
node:internal/modules/run_main:107
triggerUncaughtException(
^
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/to/app/node_modules/neko-utils/format' is not supported resolving ES modules imported from /path/to/app/src/index-pkg.js
Did you mean to import "neko-utils/format/index.js"?
今度は、メッセージの下に Did you mean to import "neko-utils/format/index.js"? と、書き換え先の候補が出ました。Node.js 22.23.4 でも同じ候補が出ました。候補のとおり 'neko-utils/format/index.js' と書き換えると、NEKO と表示されました。
パッケージを作る側なら、package.json の "exports" にサブパスを書く方法で、フォルダー名のまま読める入口を作れます。公式のエラー一覧にある ERR_UNSUPPORTED_DIR_IMPORT の説明でも、フォルダーを import する代わりに exports で名前を決める方法が示されています。neko-utils の package.json を次のようにして、入れ直しました。
{
"name": "neko-utils",
"version": "1.0.1",
"type": "module",
"exports": {
".": "./index.js",
"./format": "./format/index.js"
}
}
使う側の import { shout } from 'neko-utils/format'; を書き換えないまま実行すると、Node.js 24.22.1 と 22.23.4 のどちらでも NEKO と表示されました。
書き換え先の候補が出るときと出ないとき
Did you mean の行は、いつも出るわけではありませんでした。検証環境で試した結果は次のとおりです。
| 書き方 | 候補の行 |
|---|---|
ES モジュールのファイルで import { greet } from './lib' |
出ない |
ES モジュールのファイルで await import('./lib') |
出ない |
CommonJS のファイルで import('./lib') |
出る("./lib/index.js") |
ES モジュールのファイルで import … from 'neko-utils/format' |
出る("neko-utils/format/index.js") |
CommonJS のファイルで試すときは、package.json を "type": "commonjs" にした別のフォルダーに、次の1行だけの dynamic.js を用意しました。
import('./lib').then((m) => console.log(m.greet('neko')));
node dynamic.js を実行したときの出力から、エラーの2行だけを抜き出すと次のとおりです(パスは例示用の値に置き換えています)。
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/path/to/cjs-app/lib' is not supported resolving ES modules imported from /path/to/cjs-app/dynamic.js
Did you mean to import "./lib/index.js"?
候補の行が出なくても、直し方は同じです。Directory import '…' に出ているフォルダーの中を見て、読みたいファイルの名前を import に書きます。
フォルダーの中を探す仕組みは require にしかない
同じ CommonJS のフォルダーで、require('./lib') と書いた index.js を実行すると、lib/index.js が読まれて hello, neko と表示されました。require でフォルダーを読むときの説明によると、require() はフォルダーの package.json の main を見て、なければ index.js や index.node を探します。
ES モジュールの import には、この探し方がありません。同じ説明の中で、import('./some-library') で読み込むと ERR_UNSUPPORTED_DIR_IMPORT になると書かれています。フォルダーを読むこの仕組みは「Legacy(古い仕組み)」とされていて、代わりに exports や imports を使うよう勧められています。require('./lib') を import に書き換えるときは、'./lib/index.js' まで書くか、パッケージの入口なら exports で決めておく、と覚えておくと迷いません。





