結論:Node.jsでJSONをES Modulesとして読むときは、JSONであることを示すimport属性が必要です。static importはwith { type: 'json' }、dynamic importは第2引数に{ with: { type: 'json' } }を指定します。属性なしでERR_IMPORT_ATTRIBUTE_MISSINGになった人や、古いassert構文が失敗する人向けの手順です。
最初に、bundler設定上の想定ではなく、実際にエラーを出したNode.jsの版と最初の失敗行を確認します。自分のコードか依存コードかで対応が変わります。

再現用のsettings.json
以下を各例で共通使用します。次の内容をsettings.jsonとして、各例の.mjsファイルと同じディレクトリへ保存します。実測環境はNode.js v24.19.0です。Node.js 22系は未実測なので、公式履歴と手元のminor版を照合してください。
{"service":"brief-demo","retry":3}属性なしと旧assertが失敗する理由
Node.js公式のエラー説明では、必要なimport属性が欠け、対象モジュールを読み込めない状態がERR_IMPORT_ATTRIBUTE_MISSINGです。JSON importにはtype: 'json'が必要です。
属性なしの例は、Node.js v24.19.0で非ゼロ終了し、ERR_IMPORT_ATTRIBUTE_MISSINGになりました。
import settings from './settings.json';
console.log(settings.retry);古いimport assertionsの例は、同じ環境で非ゼロ終了し、SyntaxError: Unexpected identifier 'assert'になりました。
import settings from './settings.json' assert { type: 'json' };
console.log(settings.retry);調査基準日2026年10月5日の現行Node.js v26.10.0公式ESM資料は、Node.js 22.0.0でimport assertionsを削除したと記載しています。旧assertと現行withを混在させません。
1. 実行版と失敗ファイルを確認する
node --version
node -p "process.execPath"- Node.jsの版と、実際に呼ばれた実行ファイルのパスを控える
- 対象が
.mjsか、package.jsonに"type": "module"があるか確認する - 最初のエラー行が自分のソースか
node_modules内か確認する
"type": "module"はJavaScriptをES Modulesとして扱う設定です。JSONであることを示すwith { type: 'json' }とは別物で、前者があってもimport属性は省略できません。
2. staticとdynamicに合うwith構文へ直す
| 読み込み方 | 古い・不足した書き方 | 修正後 |
|---|---|---|
| static import | assert { type: 'json' }または属性なし | with { type: 'json' } |
| dynamic import | 第2引数なし、またはassertオプション | { with: { type: 'json' } } |
static importの修正例です。次をsettings.jsonと同じディレクトリのnode-static-fixed.mjsへ保存します。Node.js v24.19.0で3、終了コード0を確認しました。
import settings from './settings.json' with { type: 'json' };
console.log(settings.retry);dynamic importでは属性を第2引数に置き、JSONの値はdefaultから取得します。次をsettings.jsonと同じディレクトリのnode-dynamic-fixed.mjsへ保存します。こちらも同環境で3、終了コード0でした。
const module = await import('./settings.json', { with: { type: 'json' } });
console.log(module.default.retry);Import Attributesが非実験扱いになったのは、Node.js 23.1.0、22.12.0、20.18.3、18.20.5です。「Node.js 20なら全部同じ」とせずminorまで確認します。
3. 依存コードで失敗した場合
最初の失敗行が依存パッケージ内なら、自分のimport文だけ直しても解決しません。対象ライブラリの対応版、変更履歴、公式issueを確認し、ロックファイル方針に沿って更新します。Storybook公式リポジトリにも具体的な報告例がありますが、これは依存側調査の例であり、一般構文の根拠はNode.js公式資料です。
- ロックファイルを含めて更新結果を再現できるようにする
node_modulesを直接恒久編集しない- 古いEOL版Node.jsへ無条件に戻さない
4. fsとJSON.parseで読む代替
import属性を使えない事情がある場合は、UTF-8で読み込んで解析できます。Node.js v24.19.0で3、終了コード0を確認しました。
import { readFile } from 'node:fs/promises';
const text = await readFile(new URL('./settings.json', import.meta.url), 'utf8');
const settings = JSON.parse(text);
console.log(settings.retry);これは非同期なのでawaitが必要です。new URL(..., import.meta.url)は現在のモジュール基準ですが、相対文字列を直接readFileへ渡すと通常はカレントディレクトリ基準です。読取失敗とJSON構文不正は別の例外になるため、必要な境界で処理します。また、importのモジュールキャッシュとは異なり、再読込を避けるなら独自キャッシュが必要です。
5. 同じNode.jsで再確認する
node --version
node node-static-fixed.mjs
node node-dynamic-fixed.mjs検証環境では順にv24.19.0、3、3でした。修正前と同じNode.js実行ファイルで成功し、終了コード0になったかまで確認します。
よくある質問
Q. ERR_IMPORT_ASSERTION_TYPE_MISSINGと同じですか?
近い原因を示す旧エラーです。公式資料では同エラーはv21.1.0で削除され、ERR_IMPORT_ATTRIBUTE_MISSINGはv21.1.0で追加されています。実行版に合う構文を選びます。
Q. package.jsonをtype: moduleにすれば直りますか?
直りません。モジュール形式の指定とJSONのimport属性は役割が異なります。
Q. Node.js 22でも同じ結果ですか?
この記事の実測はv24.19.0です。公式履歴では22.0.0で旧assertが削除され、22.12.0でImport Attributesが非実験化されています。使用中の22.xで確認してください。
Q. 依存が直るまでnode_modulesを書き換えてよいですか?
恒久対応にはしません。対応版、公式パッチ、package managerの正式なパッチ機能など、再現できる方法を選びます。

コメント