Error [Unexpected identifier 'assert']

SyntaxError: Unexpected identifier 'assert':JSON import の assert を with に直す(Node.js)

FIX SUMMARY verified
Applies when
node:22-alpineJSON import assert attribute on Node 22+

Verified: reproduced in node:22-alpine, then the Unexpected identifier 'assert' signature was gone after the fix (exit 0).

手元では動いていたコードを 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 と表示されるため、版を上げるまで問題に気づけません。

解決:assertwith に書き換える

キーワードを 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 definedrequire is not defined in ES module scope)や、相対 import の拡張子・ディレクトリ関連(ERR_MODULE_NOT_FOUND / ERR_UNSUPPORTED_DIR_IMPORT)も一緒に出ることがあります。原因はどれも同じ(ESM 扱いになったこと)です。

検証(machine-verified)

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

verify — run-case.mjs
$ node run-case.mjs node/import-attributes-assert-to-with
● reproduce Unexpected identifier 'assert' present ✓
● apply fix exit 0
● re-run Unexpected identifier 'assert' gone ✓
PASS verified · node:22-alpine · signature gone