手元では動いていたコードを Node 22 以降で実行したら、次のエラーで落ちることがあります。
file:///app/index.js:1
import data from './data.json' assert { type: 'json' };
^^^^^^
SyntaxError: Unexpected identifier 'assert'
原因は、JSON を import するときの型指定の構文が assert から with に変わり、Node 22 で古い assert が削除されたことです。assert { type: 'json' } は古い書き方(import assertions)で、import data from './data.json' with { type: 'json' }(import attributes)が新しい書き方です。Node 18・20 では assert も警告つきで動くので、その版で書いたコードを Node 22 以降に上げた瞬間、パースの段階で SyntaxError になって落ちます。
assert の位置で構文エラーになっているので、これは実行時ではなく**読み込み時(構文解析時)**のエラーです。この import 文と同じファイルの中で try/catch で囲んでも、モジュール本体が動き出す前の解析で落ちるので捕まえられません(別ファイルから await import('./bad.js') と動的に読み込む場合は、呼び出し側の try/catch で受け取れます。ただしそれは握りつぶすだけで、構文自体は直す必要があります)。
なぜ Node 22 で急に落ちるのか(版境界)
JSON import の型指定は、仕様側で assert キーワードから with キーワードへ置き換えられました。Node はこれに追随して、with を先に追加し、あとから assert を削除しています。同じ assert { type: 'json' } でも、動かした Node の版で結果が変わります。
| Node の版 | assert { type: 'json' } | with { type: 'json' } |
|---|---|---|
| 18.19 / 20.9 以前 | 動く(実験的機能の警告) | SyntaxError: Unexpected token 'with'(まだ未対応) |
| 18.20 / 20.10 / 21 〜 | 動く(警告が出る) | 動く |
| 22 以降 | SyntaxError: Unexpected identifier 'assert' | 動く |
with が構文として使えるようになったのは Node 18.20 / 20.10 / 21.0 以降で、それより前の版では with の側が SyntaxError: Unexpected token 'with' になります。逆に assert が削除されたのは Node 22.0.0 です。つまり両方が動くのは 18.20〜21 の帯だけで、その前後では「片方だけ」しか通りません。
assert を動かしたときに出る警告は版によって文言が違い、新しめの 18・20 では次の「予告」が出ます。
(node:1) V8: file:///app/index.js:1 'assert' is deprecated in import statements and support will be removed in a future version; use 'with' instead
この落とし穴は版差そのものです。開発マシンがまだ Node 20 で、CI や本番が Node 22 に上がっている、という食い違いで「手元では動くのに CI でだけ SyntaxError」という形になります。
再現(最小構成)
package.json を ESM 指定にします(JSON import の型属性は ESM の機能です)。
{
"type": "module"
}
読み込む JSON と、それを古い assert で import するコードを置きます。
// data.json
{
"greeting": "hello"
}
// index.js(type:module 下なので ESM 扱い)
import data from './data.json' assert { type: 'json' };
console.log(data.greeting);
これを Node 22 以降で node index.js すると SyntaxError: Unexpected identifier 'assert' が出て、終了コードは 1 になります。Node 18・20 では警告つきで hello と表示されるため、版を上げるまで問題に気づけません。
解決:assert を with に書き換える
キーワードを assert から with に変えるだけです。中身({ type: 'json' })は同じです。
// index.js(ESM のまま。assert → with)
import data from './data.json' with { type: 'json' };
console.log(data.greeting);
with が使える Node 18.20 / 20.10 / 21.0 以降なら、これで終了コード 0 で動きます。いま保守されている版のほとんどは with に統一して問題ありません(ごく古い 18.19 以前などにも対応する必要がある場合だけは、後述の切り分けを参照)。
なお JSON の import 自体が長く実験的機能だった名残で、境界に近い版(18.20.0・20.10.0 など)は with でも ExperimentalWarning: Importing JSON modules is an experimental feature ... が出ます。警告まで消えるのは 18.20.5 / 20.18.3 / 22.12 以降です。エラーではないので動作には影響しませんが、stderr を厳しく見る CI では拾われることがあります。
動的 import でも同じで、第2引数のオプションを with に書きます。
const { default: data } = await import('./data.json', { with: { type: 'json' } });
切り分け(うまくいかないとき)
-
withに直したら別のエラー(ERR_IMPORT_ATTRIBUTE_MISSINGなど)が出た:型属性そのものを書き忘れているときのエラーです。import data from './data.json'のようにwith { type: 'json' }が無いと、Node 22・24 ではTypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "..." needs an import attribute of "type: json"、Node 18・20 では同じ状況でERR_IMPORT_ASSERTION_TYPE_MISSING(エラー名も版で変わります)になります。どちらも対処は同じで、with { type: 'json' }を書き足します。 -
古い Node(18.19 以前など)にも対応したい:その版は
withを知らないため、with側で構文エラーになります。古い版まで面倒を見るなら、JSON import をやめてfs.readFileSync+JSON.parseで読むのが確実です(版に依存しません)。import { readFileSync } from 'node:fs'; const data = JSON.parse(readFileSync(new URL('./data.json', import.meta.url), 'utf8')); -
TypeScript を使っている:エディタや
tscの版が古いとwithを知らず、assertのままにされることがあります。TypeScript 5.3 以降がwithに対応しています。出力される JS がassertになっていないか確認してください。 -
同じ ESM 化で別のエラーも出ている:
type: "module"にした途端にrequire is not defined(require is not defined in ES module scope)や、相対 import の拡張子・ディレクトリ関連(ERR_MODULE_NOT_FOUND / ERR_UNSUPPORTED_DIR_IMPORT)も一緒に出ることがあります。原因はどれも同じ(ESM 扱いになったこと)です。