Error [ERR_IMPORT_ATTRIBUTE_MISSING]

ERR_IMPORT_ATTRIBUTE_MISSING の直し方(ESM の JSON import に with { type: "json" } が要る)

FIX SUMMARY verified
Applies when
node:22.23.1-alpineNode.js ESM (JSON import requires a with { type: "json" } attribute; ERR_IMPORT_ASSERTION_TYPE_MISSING on 18/20, ERR_IMPORT_ATTRIBUTE_MISSING on 22/24)importing JSON in ESM without an import attribute — typically after moving from CommonJS require or from a bundler that allowed attribute-less JSON imports

Verified: reproduced in node:22.23.1-alpine, then the ERR_IMPORT_ATTRIBUTE_MISSING signature was gone after the fix (exit 0).

ESM("type": "module".mjs)で JSON をそのまま import すると、次のエラーで止まることがあります。

node:internal/modules/esm/assert:88
        throw new ERR_IMPORT_ATTRIBUTE_MISSING(url, 'type', validType);
              ^

TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///app/data.json" needs an import attribute of "type: json"
    at validateAttributes (node:internal/modules/esm/assert:88:15)

終了コードは 1 です。Node 18・20 では、同じ状況でエラー名が ERR_IMPORT_ASSERTION_TYPE_MISSING になります(後述)。

原因は、ESM で JSON を import するには with { type: "json" } という import attribute(型属性)が必須で、それを書いていないことです。属性なしの import data from "./data.json" は、CommonJS の require("./data.json") やバンドラ経由なら通っていた書き方ですが、Node の素の ESM では通りません。直し方は、import 文に with { type: "json" } を足すことです。

// 旧:属性なし(ESM では ERR_IMPORT_ATTRIBUTE_MISSING)
// import data from "./data.json";

// 新:type: json の import attribute を付ける
import data from "./data.json" with { type: "json" };

console.log(data.answer);

なぜ起きるのか:ESM は JSON を「型を明示して」読む

JavaScript のモジュール仕様は、JSON や今後の別形式を import するとき、何として解釈するかを import 側が明示する仕組み(import attributes)を導入しました。with { type: "json" } が「このモジュールは JSON として読む」という指定です。Node の ESM ローダーは、JSON を読むときこの属性を要求し、無ければモジュールを読み込む前(validateAttributes の段階)で ERR_IMPORT_ATTRIBUTE_MISSING を投げます。

エラーがコード本体の実行ではなく読み込み時に出るのは、import の解決がモジュール評価より前に走るためです。console.log などの行までは到達していません。

なぜ ESM でだけ出るのか

境界は モジュールの種類(CommonJS か ESM か)とツールチェーンで、Node の版ではありません。同じ JSON 読み込みでも、どこで実行するかで属性の要否が変わります。

  • CommonJS から ESM へ移した:CommonJS の require("./data.json") は属性なしで JSON を読めます。"type": "module" を付けた、あるいは .mjs にした瞬間、同じ import に with { type: "json" } が要るようになります。ESM 化にともなって表面化する型の一つです。
  • バンドラ(webpack / Vite など)では書かずに通っていた:バンドラは属性なしの JSON import を独自に解決します。同じコードを Node で直接動かす(テスト・SSR・スクリプト実行など)と、素の ESM ローダーが属性を要求します。「ビルドは通るのに node で実行すると落ちる」形になります。

Node 18・20 でも属性なしは同じくエラー(終了コード 1)ですが、エラー名が ERR_IMPORT_ASSERTION_TYPE_MISSING です。これは属性の仕様が assert から with に置き換わった名残で、名前が違うだけで対処は同じ(属性を書き足す)です。Node 22 以降は ERR_IMPORT_ATTRIBUTE_MISSING に変わりました。

直し方:with { type: “json” } を書き足す

static import なら、import 文の末尾に with { type: "json" } を付けます。

import data from "./data.json" with { type: "json" };

dynamic import なら、第2引数のオプションに with を渡します。

const { default: data } = await import("./data.json", { with: { type: "json" } });

with 構文が使えるのは Node 18.20 / 20.10 / 21 以降です。それより古い版では with 自体が構文エラー(SyntaxError: Unexpected token 'with')になるので、そのときは JSON import をやめて fs で読むのが版に依存しない方法です。

import { readFileSync } from "node:fs";
const data = JSON.parse(readFileSync(new URL("./data.json", import.meta.url), "utf8"));

切り分け(うまくいかないとき)

  • エラー名が assert を含む(SyntaxError: Unexpected identifier 'assert':それは属性を書いていないのではなく、古い assert 構文で書いているケースです。assert { type: "json" }with { type: "json" } に直します。詳しくは JSON import の assert を with に直す を参照してください。本記事は属性が「無い」ケース、あちらは属性を「古い書き方で書いている」ケースです。
  • with { type: "json" } を足したら ExperimentalWarning: Importing JSON modules is an experimental feature が出る:境界に近い版(18.20.0・20.10.0 など)では、正しく書いても実験的機能の警告が出ます。警告まで消えるのは、系統ごとに 18.20.5+ / 20.18.3+ / 22.12.0+ / 23.1.0+ です(「22.12 以降」とひとまとめに読むと、23.0.x で警告が消えていると誤解します。23 系は 23.1.0 からです)。エラーではないので動作には影響しませんが、stderr を厳しく見る CI では拾われます。
  • TypeScript で書いているwith 構文に対応するのは TypeScript 5.3 以降です。5.3 未満の tscwith を構文として受け付けず、コンパイル段階でエラーになります(withassert に変換するわけではありません)。その場合は TypeScript を更新します。逆に、入力を古い assert で書いていると、TypeScript はそれをそのまま出力に残すので、出た JS を Node 22 以降で実行すると落ちます(入力を with に直します)。resolveJsonModule は tsc の型解決の設定で、実行時に Node が求める属性とは別なので、resolveJsonModule: true でもこのエラーは出ます。
  • 同じ ESM 化で別のエラーも一緒に出ている"type": "module" にした途端に require is not defined in ES module scope や、相対 import の拡張子・ディレクトリ関連(ERR_MODULE_NOT_FOUND / ERR_UNSUPPORTED_DIR_IMPORT)も出ることがあります。原因はどれも同じ(ESM 扱いになったこと)です。

検証環境

  • node:22.23.1-alpine、ネットワーク不要
  • 再現:"type": "module" の下で import data from "./data.json";(属性なし)を実行すると、TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "..." needs an import attribute of "type: json" で終了コード 1
  • 修正:同じ import に with { type: "json" } を足すと、終了コード 0・シグネチャ消滅(42 を出力)

再現から修正までは errfix の検証ハーネスが機械的に確認しています。reproduce と fix の差は import 文の with { type: "json" } の有無だけです。エラー名が Node 18.20.8 / 20.20.2 では ERR_IMPORT_ASSERTION_TYPE_MISSING、22.23.1 / 24.18.0 では ERR_IMPORT_ATTRIBUTE_MISSING になること(いずれも属性なしは終了コード 1)は、各版のイメージで実行して確認し、verification/probes.txt に記録しています。

検証(machine-verified)

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

verify — run-case.mjs
$ node run-case.mjs node/json-import-attribute-required
● reproduce ERR_IMPORT_ATTRIBUTE_MISSING present ✓
● apply fix exit 0
● re-run ERR_IMPORT_ATTRIBUTE_MISSING gone ✓
PASS verified · node:22.23.1-alpine · signature gone

確認したのは上のイメージの中だけです。別の環境で直らなかった、記述が違う、という場合は 報告してください(対象と検証イメージは件名・本文に入ります)。