Fix ERR_IMPORT_ASSERTION_TYPE_MISSING when importing JSON
Match the import form to the Node.js release that runs your code, make tsc flag the missing attribute at build time, and handle the two errors that come next.
TL;DR#
Since Node.js 17.1.0 and 16.14.0, an ES module that imports a .json file must say that it expects JSON. Without a type: 'json' attribute on the import, Node.js throws ERR_IMPORT_ASSERTION_TYPE_MISSING, and Node.js 22 and later print the same error as ERR_IMPORT_ATTRIBUTE_MISSING. Add with { type: 'json' } to the import. The assert { type: 'json' } form from older answers stops working on Node.js 22: releases 22, 24 and 26 reject it with a SyntaxError. If one codebase must run both on releases that only know assert and on 22 or later, load the file with createRequire or readFileSync instead of a JSON module.
The same error in three wordings#
The Stack Overflow question behind this page hit it after an upgrade to Node.js 17. A TypeScript server, compiled to dist/, imported ./data/countries.json from api.ts and was started with NODE_ENV=production and the flags --optimize_for_size --trace-warnings --experimental-json-modules --no-warnings. Rebuilding that layout and running the asker's exact command on Node.js 17.9.1 prints:
TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "file:///app/dist/server/data/countries.json" needs an import assertion of type "json"The message depends on the release that runs the code. Reproduced in the official node Docker images on 6 October 2026, with the same JavaScript files on every release:
| Node.js (tested) | No attribute | assert { type: 'json' } | with { type: 'json' } |
|---|---|---|---|
| 17.9.1, 20.9.0 | ERR_IMPORT_ASSERTION_TYPE_MISSING, "import assertion" wording | runs, with an ExperimentalWarning | SyntaxError: Unexpected token 'with' |
| 18.19.1 | ERR_IMPORT_ASSERTION_TYPE_MISSING, "import attribute" wording | runs, with an ExperimentalWarning | SyntaxError: Unexpected token 'with' |
| 18.20.8, 20.10.0, 20.20.2 | ERR_IMPORT_ASSERTION_TYPE_MISSING, "import attribute" wording | runs (18.20.8 and 20.20.2 add a V8 deprecation warning) | runs |
| 22.23.3, 24.21.0, 26.10.0 | ERR_IMPORT_ATTRIBUTE_MISSING | SyntaxError: Unexpected identifier 'assert' | runs |
On Node.js 22.23.3, 24.21.0 and 26.10.0 the line reads:
TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///app/dist/server/data/countries.json" needs an import attribute of "type: json"The wording narrows down which syntax your runtime accepts, but it does not settle it. On the releases tested here, "import assertion" came from releases that only understand assert (17.9.1, 20.9.0), and "import attribute" mostly from releases where with works (18.20.8, 20.10.0 and later). The exception is 18.19.x: Node.js 18.19.1 already says "import attribute" but has no with, and the Node.js 18.19.0 documentation says so plainly: "Because the version of V8 on this release line does not support the with keyword, you need to keep using assert to support this version of Node.js." Keep assert there, and go by the version rather than the message. The --experimental-json-modules flag in the asker's command makes no difference either way: every release tested still accepts it, and the error is identical with and without it.
Why Node.js refuses a JSON import without a type#
In an ES module, Node.js only loads a .json file as a JSON module when the import itself declares that it expects JSON. The Node.js ES modules documentation puts it in one line: "The type: 'json' attribute is mandatory when importing JSON modules." The marker comes from the browser side of the standard: the import attributes proposal records that browser vendors asked for "some syntactic marker" on JSON imports "to prevent a scenario where the responding server unexpectedly provides a different MIME type, causing code to be unexpectedly executed". Node.js enforces the same marker for files on disk.
The rule is newer than many codebases. The esm.md history dates import assertions to Node.js 17.1.0 and 16.14.0, and the errors reference gives ERR_IMPORT_ASSERTION_TYPE_MISSING the same two releases. Before that, the Node.js 17.0.0 documentation still showed a bare import packageConfig from './package.json'; working under --experimental-json-modules. That fits the asker's "This code was working fine": the import compiled, the runtime changed.
TypeScript does not add the attribute for you. With "module": "esnext" and "moduleResolution": "bundler", TypeScript 7.0.2 exited with code 0 and wrote import countryTable from './data/countries.json'; into dist/api.js exactly as written; Node.js then refused the output at runtime.
The keyword changed once more after that. The proposal became import attributes, which use with. The accepted answer's update says the new keyword is with "but assert is supposed to stay for compatibility reasons". Node.js did not keep it: nodejs/node#52104 dropped import assertions in Node.js 22, and the parser now rejects the keyword, so the module never runs:
SyntaxError: Unexpected identifier 'assert'Node.js 20.20.2 still runs the assert form, but warns that it is on its way out:
(node:45) V8: file:///app/dist/server/api.js:1 'assert' is deprecated in import statements and support will be removed in a future version; use 'with' insteadFix: add the attribute your Node.js version understands#
1. Read the version where the compiled code runs#
node --versionRun it in the container, CI job or server that executes dist/, not only on your laptop. In a Docker image, the release that parses your import is the one in the FROM line of the stage that runs the code. The multi-stage Dockerfile in Fix: sh: tsc: not found in Docker build compiles TypeScript in a build stage and copies dist/ into a separate runtime stage, so check that second image.
2. Static imports: add with { type: 'json' }#
// src/api.ts (or the .js file Node.js runs)
import countryTable from './data/countries.json' with { type: 'json' };
export const countryCount = Object.keys(countryTable).length;On 18.20.8, 20.10.0, 20.20.2, 22.23.3, 24.21.0 and 26.10.0 the server then started and printed countries loaded: 3. On the 18 line, the Node.js docs date with to 18.20.0; 18.19.1 still rejects it (step 4).
3. Dynamic import(): the option is called with too#
const { default: countryTable } = await import('./data/countries.json', {
with: { type: 'json' },
});Watch the option name in both directions, because neither mistake is a syntax error. On Node.js 22.23.3, 24.21.0 and 26.10.0, the assert key in import('./data/countries.json', { assert: { type: 'json' } }) is not read, and the import fails with ERR_IMPORT_ATTRIBUTE_MISSING, exactly as if no attribute had been passed. The mirror case hits releases without with: on 17.9.1, 18.19.1 and 20.9.0 the with key is not read either, and the import fails with the original ERR_IMPORT_ASSERTION_TYPE_MISSING. Pass { assert: { type: 'json' } } there. Search the codebase for assert: as well as assert {.
4. Releases without with: use assert, or no JSON module at all#
On Node.js 17.9.1, 18.19.1 and 20.9.0, a static with import is itself the error:
SyntaxError: Unexpected token 'with'On the 18 and 20 lines, updating within the line is enough: 18.20.8 and 20.10.0 accept with. The 17 line never got it: 17.9.1, its last release, rejects it. If you cannot update, assert { type: 'json' } runs on those releases, but it breaks again the day the code moves to Node.js 22. The version-proof option is not to use a JSON module at all. module.createRequire() gives an ES module a CommonJS require, which parses a .json file without any attribute:
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const countryTable = require('./data/countries.json');Or read and parse the file yourself:
import { readFileSync } from 'node:fs';
const countryTable = JSON.parse(
readFileSync(new URL('./data/countries.json', import.meta.url), 'utf8'),
);Both ran unchanged on every release tested, from 17.9.1 to 26.10.0. Both resolve the path from the module's own location, not from the working directory: the server was started from dist/, where ./data/countries.json does not exist, and both still found dist/server/data/countries.json next to api.js.
Make tsc reject the bare JSON import before Node.js does#
With bundler settings, tsc compiles a bare JSON import without complaint: the bundler build above exited with code 0. The TypeScript 6.0 release notes tell projects leaving the deprecated node10 resolution to pick nodenext "if they plan on targeting Node.js directly", and under "module": "nodenext" the compiler knows the rule:
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"rootDir": "src",
"outDir": "dist",
"strict": true,
"skipLibCheck": true
},
"include": ["src"]
}With that tsconfig.json, the bare import stops at compile time:
src/api.ts(1,26): error TS1543: Importing a JSON file into an ECMAScript module requires a 'type: "json"' import attribute when 'module' is set to 'NodeNext'.TypeScript 5.7.2 and 7.0.2 both reported TS1543; TypeScript 5.6.3 compiled the same project without a word, because the check arrived with TypeScript 5.7. Under nodenext, TypeScript 7.0.2 also rejects the assert form, a restriction the TypeScript release notes tie to Node.js 22 dropping it:
src/api.ts(1,50): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.If module is set to an older target such as es2022, the attribute itself is refused:
src/api.ts(1,50): error TS2823: Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.Two details change what you see. First, on each of those errors tsc exited with code 2 and still wrote dist/api.js; after TS1543, that file held the bare import and failed at runtime with ERR_IMPORT_ATTRIBUTE_MISSING. A build script that ignores the exit code ships the broken file, and the runtime error comes back. Second, resolveJsonModule does not add the attribute; it only lets tsc resolve the .json file at all. When the option is left out, the TypeScript 7.0.2 compiler turns it on by itself for nodenext and for bundler resolution, and it compiled the attribute import without it. TypeScript 5.6.3 and 5.7.2 did not under nodenext (the only resolution the reproduction tried on 5.x), which is why the tsconfig.json above sets it:
src/api.ts(1,26): error TS2732: Cannot find module './data/countries.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.If the move to nodenext is part of a TypeScript 6 upgrade, TypeScript 6.0 migration: what actually breaks covers the other changes in that release, such as strict mode on by default and the removal of classic module resolution.
Running the .ts file directly does not escape the rule either. On Node.js 24.21.0, node main.ts with a bare JSON import failed with the same ERR_IMPORT_ATTRIBUTE_MISSING, and adding with { type: 'json' } fixed it.
Check your own JSON import error#
Paste the full error into the explainer below, and enter the Node.js version that printed it: releases on both sides of the assert/with change print the same ERR_IMPORT_ASSERTION_TYPE_MISSING code, 18.19.1 and 18.20.8 even print the same wording, and the version decides which import form it recommends. It runs in your browser; nothing is sent.
Runs entirely in your browser — nothing you paste is sent anywhere. Tokens and passwords that appear in paths or URLs are masked in the output.
Optional: package.json, file extension, Node.js version (sharper advice)
Verify the fix#
Make the build fail when tsc fails, then start the server the way production does. The asker's flags can stay:
npx tsc -p tsconfig.json && NODE_ENV=production node --optimize_for_size --trace-warnings --experimental-json-modules --no-warnings dist/server.jsWith the tsconfig.json above, src/api.ts and src/server.ts, the bare import stopped at TS1543 and node never started. With the attribute in place, the same line printed:
countries loaded: 3Then run the same check with the Node.js release your deployment uses, whether that is an .nvmrc, a CI matrix or a Docker tag. An import that works on Node.js 20 with assert is the same import that fails on 22.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
When the JSON file lives in a dependency#
Your own files import fine once the attribute is there: import pkg from './package.json' with { type: 'json' } printed the package name on Node.js 24.21.0. A file inside a dependency is different, because the specifier goes through that package's exports map, and the attribute does nothing about that. chalk 6.0.1 lists only its entry point, so importing its package.json changes the error to:
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './package.json' is not defined by "exports" in /dep/node_modules/chalk/package.json imported from /dep/dep-package.jscreateRequire does not get around it: require('chalk/package.json') hit the same ERR_PACKAGE_PATH_NOT_EXPORTED on 22.23.3 and 24.21.0. What worked on both releases was asking Node.js where the file is with module.findPackageJSON(), which the docs still mark as active development, and reading it:
import { findPackageJSON } from 'node:module';
import { readFileSync } from 'node:fs';
const chalkPackagePath = findPackageJSON('chalk', import.meta.url);
const chalkPackage = JSON.parse(readFileSync(chalkPackagePath, 'utf8'));Node.js 20.20.2 does not have the function yet, so on the 20 line this snippet fails before it runs:
SyntaxError: The requested module 'node:module' does not provide an export named 'findPackageJSON'For how the exports map decides which paths a package exposes, and the second cause behind the same message, continue with Fix ERR_PACKAGE_PATH_NOT_EXPORTED: subpath not in exports.
A JSON module has no named exports#
Once the attribute is right, import { gb } from './data/countries.json' with { type: 'json' } fails on every release that accepts with:
SyntaxError: The requested module './data/countries.json' does not provide an export named 'gb'The JSON modules section of the Node.js docs says why: "The imported JSON only exposes a default export. There is no support for named exports." Import the default and destructure it:
import countryTable from './data/countries.json' with { type: 'json' };
const { gb } = countryTable;The error is the runtime asking the importer to vouch for the file: say with { type: 'json' } on Node.js 18.20.x and on 20.10.0 and later, read the version where the code actually runs before trusting an assert answer, and let tsc with nodenext catch the bare import before it reaches production. When a single codebase has to span releases on both sides of that change, createRequire or readFileSync avoids the question entirely.
Further reading: Fix TS2882: side-effect import of './globals.css'.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix An index signature parameter type cannot be a union
TypeScript blocks union types in index signatures. Learn why the error fires and how to fix it with mapped types or the Record utility type in under a minute.
Fix Cannot find type definition file for 'body-parser'
Why one broken folder under node_modules/@types fails the whole project, why restarting VS Code sometimes clears it, and what TypeScript 6.0 changed.
Fix TS7053: Element Implicitly Has an Any Type When Indexing
TypeScript throws TS7053 when a plain string indexes a typed object. Use keyof typeof, index signatures, or Record to fix it safely.


