Node.jsのERR_IMPORT_ATTRIBUTE_MISSINGの直し方

​​Node.js​ES Modules,JavaScript,JSON,トラブルシュート

Node.js の ES モジュールで import config from './config.json' と書き、設定ファイルやデータの JSON を読み込もうとすると、TypeError [ERR_IMPORT_ATTRIBUTE_MISSING] が出て止まります。Node.js では、JSON を import するときに with { type: 'json' } を書くことが必須だからです。これを付け足すと、エラーは消えます。

私は、JSON を1つ読むだけの小さなファイルで実際にこのエラーを起こし、出たメッセージの全文と、直したあとに消えることを確かめました。あわせて、以前の書き方の assert { type: 'json' } が新しい Node.js では動かないことや、JSON から名前を指定して取り出せないことも試しています。

補足

検証環境は Ubuntu 24.04 で、Node.js 20.18.3、22.23.4、24.22.1 で同じファイルを実行しました。20.18.3 は nodejs.org で配布されている Linux 用のバイナリを使いました。Windows と macOS での動き、TypeScript を変換してから実行する場合、webpack や Vite などのバンドラーを通した場合は試していません。

エラーを起こしたファイルと、出たメッセージの全文

package.json に "type": "module" を書いて、.js のファイルを ES モジュールとして扱うようにしました。読み込む JSON は、次の config.json です。

json
{ "name": "neko", "port": 3000 }

これを index.js から import します。

js
import config from './config.json';

console.log(config.name);

Node.js 24.22.1 で node index.js を実行すると、次のように出て止まりました(終了コードは1。コマンドは node だけに縮め、パスは例示用の値に置き換えています)。

text
$ node index.js
node:internal/modules/esm/assert:110
        throw new ERR_IMPORT_ATTRIBUTE_MISSING(url, 'type', validType);
              ^

TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///path/to/app/config.json" needs an import attribute of "type: json"
    at validateAttributes (node:internal/modules/esm/assert:110:15)
    at defaultLoadSync (node:internal/modules/esm/load:163:3)
    at #loadAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:806:12)
    at #loadSync (node:internal/modules/esm/loader:838:53)
    at ModuleLoader.load (node:internal/modules/esm/loader:787:26)
    at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:498:31)
    at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:564:36)
    at afterResolve (node:internal/modules/esm/loader:611:52)
    at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:617:12)
    at ModuleJob.syncLink (node:internal/modules/esm/module_job:276:33) {
  code: 'ERR_IMPORT_ATTRIBUTE_MISSING'
}

Node.js v24.22.1

TypeError で始まる行は、「config.json というモジュールには、type: json というインポート属性(import attribute)が必要です」という意味です。インポート属性は、import の文に、読み込むファイルについての情報を書き足すための書き方です。

この出力には、どの import の文で止まったのかが出ていません。表示されている場所(node:internal/…)とスタックは、どれも Node.js の内部のものです。手がかりになるのは、Module "file:///…" に出ている JSON ファイルの名前です。プロジェクトの中でそのファイル名を import している箇所を探します。

Node.js 22.23.4 でも、TypeError の行は一字一句同じでした。最初の行が node:internal/modules/esm/assert:88 になるなど、内部の場所の表示だけが違いました。

with { type: 'json' } を付けると消える

import の文の最後に with { type: 'json' } を付けた fixed.js を用意しました。

js
import config from './config.json' with { type: 'json' };

console.log(config.name);

node fixed.js は、20.18.3、22.23.4、24.22.1 のすべてで neko と表示し、終了コードは0でした。エラーも警告も出ていません。

Node.js の公式ドキュメントの、JSON モジュールの説明にも、with { type: 'json' } の書き方は必須だと書かれています。インポート属性の説明によると、Node.js が受け付ける属性は type だけです。JSON を読み込むときは、この type: 'json' を付けなければなりません。

import() で読むときは2つ目の引数に書く

await import('./config.json') のように、import() で JSON を読むときも同じエラーになります。Node.js 24.22.1 で試したところ、TypeError の行は上と同じ文面でした。

text
TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///path/to/app/config.json" needs an import attribute of "type: json"

import() の場合は、2つ目の引数に { with: { type: 'json' } } を渡します。読み込んだ JSON は default に入っています。

js
const { default: config } = await import('./config.json', { with: { type: 'json' } });

console.log(config.name);

この書き方でも、3つのバージョンすべてで neko と表示されました。

Node.js 20 ではエラーの名前が違う

同じ index.js を Node.js 20.18.3 で実行すると、エラーの名前は ERR_IMPORT_ASSERTION_TYPE_MISSING でした(パスは例示用の値に置き換えています)。

text
$ node index.js
node:internal/modules/esm/assert:89
        throw new ERR_IMPORT_ASSERTION_TYPE_MISSING(url, validType);
              ^

TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "file:///path/to/app/config.json" needs an import attribute of type "json"
    at validateAttributes (node:internal/modules/esm/assert:89:15)
    at defaultLoad (node:internal/modules/esm/load:153:3)
    at async ModuleLoader.load (node:internal/modules/esm/loader:555:7)
    at async ModuleLoader.moduleProvider (node:internal/modules/esm/loader:434:45) {
  code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING'
}

Node.js v20.18.3

エラーの名前だけでなく、メッセージの終わりも違います。22 と 24 は of "type: json"、20 は of type "json" です。原因と直し方は同じで、with { type: 'json' } を付けた fixed.js は 20.18.3 でも動きました。

公式ドキュメントのエラー一覧では、ERR_IMPORT_ATTRIBUTE_MISSING は Node.js 21.1.0 で追加されたエラーです。以前の名前の ERR_IMPORT_ASSERTION_TYPE_MISSING は、使われなくなったエラーコードの一覧に入っていて、21.1.0 で削除されたと書かれています。

assert { type: 'json' } は Node.js 22 から動かない

Node.js 22 より前は、with ではなく assert を使った以前の書き方でも JSON を読み込めました。

js
import config from './config.json' assert { type: 'json' };

console.log(config.name);

この assert.js は、Node.js 20.18.3 では neko と表示しましたが、次の警告も出ました((node:823) はプロセス番号で、パスは例示用の値に置き換えています)。

text
$ node assert.js
(node:823) V8: file:///path/to/app/assert.js:1 'assert' is deprecated in import statements and support will be removed in a future version; use 'with' instead
(Use `node --trace-warnings ...` to show where the warning was created)
neko

「import の文の assert は非推奨で、将来のバージョンで使えなくなるので、代わりに with を使うように」という意味です。実際に、Node.js 22.23.4 と 24.22.1 では、ファイルを読み込む段階で構文エラーになりました。次は 24.22.1 の出力です(at で始まる行から後は省略し、パスは例示用の値に置き換えています)。

text
$ node assert.js
file:///path/to/app/assert.js:1
import config from './config.json' assert { type: 'json' };
                                   ^^^^^^

SyntaxError: Unexpected identifier 'assert'

ES モジュールについての公式ドキュメントにある変更履歴には、Node.js 22.0.0 で import assertions(assert の書き方)のサポートをやめたと書かれています。assert を with に書き換えれば、22 と 24 でも動きます。

JSON から名前を指定して取り出すことはできない

with { type: 'json' } を付けても、import { name } from './config.json' with { type: 'json' }; のように、JSON のキーを名前で取り出す書き方はできません。3つのバージョンのすべてで、次のエラーになりました(24.22.1 の出力。at で始まる行から後は省略し、パスは例示用の値に置き換えています)。

text
$ node named.js
file:///path/to/app/named.js:1
import { name } from './config.json' with { type: 'json' };
         ^^^^
SyntaxError: The requested module './config.json' does not provide an export named 'name'

公式ドキュメントの JSON モジュールの説明にあるとおり、JSON モジュールには default のエクスポートしかなく、名前付きのエクスポートはありません。import config from … のように全体を受け取ってから、config.name で取り出します。

import 以外で JSON を読む方法

import の書き方を変えたくないときや、ファイルの場所を実行中に決めたいときは、次の2つの方法でも JSON を読めます。どちらも、3つのバージョンすべてで neko と表示されました。

ES モジュールの中で require() を使いたい場合は、module.createRequire() で require 関数を作ります。require() で読む JSON には、インポート属性は要りません。

js
import { createRequire } from 'node:module';

const require = createRequire(import.meta.url);
const config = require('./config.json');

console.log(config.name);

ファイルを文字列として読み、JSON.parse() で変換する方法もあります。

js
import { readFile } from 'node:fs/promises';

const config = JSON.parse(await readFile(new URL('./config.json', import.meta.url), 'utf8'));

console.log(config.name);
ポイント

ERR_IMPORT_ATTRIBUTE_MISSING(Node.js 20 では ERR_IMPORT_ASSERTION_TYPE_MISSING)は、JSON の import に with { type: 'json' } を付けると消えます。assert { type: 'json' } と書いている場合は、Node.js 22 以降で動くように with に書き換えます。

​Posted by 86