parcel-bundler/parcel · error · ThrowableDiagnostic
Output format "esmodule" cannot be used in the "main" target
Error message
Output format "esmodule" cannot be used in the "main" target without a .mjs extension or "type": "module" field.
What it means
The `main` target conventionally emits CommonJS for Node. If you set its outputFormat to `esmodule` but the distEntry does not use `.mjs` AND package.json has no `"type": "module"`, Node would load the output as CommonJS, breaking ESM semantics. Parcel refuses to silently produce a mis-loaded bundle. The guard fires when targetName==='main', outputFormat==='esmodule', and the inferredOutputFormat (from extension/package type) is not esmodule.
Source
Thrown at packages/core/core/src/requests/TargetRequest.js:827
let outputFormat =
descriptor.outputFormat ??
this.options.defaultTargetOptions.outputFormat ??
inferredOutputFormat ??
(targetName === 'module' ? 'esmodule' : 'commonjs');
let isModule = outputFormat === 'esmodule';
if (
targetName === 'main' &&
outputFormat === 'esmodule' &&
inferredOutputFormat !== 'esmodule'
) {
let contents: string =
typeof pkgContents === 'string'
? pkgContents
: // $FlowFixMe
JSON.stringify(pkgContents, null, '\t');
throw new ThrowableDiagnostic({
diagnostic: {
// prettier-ignore
message: md`Output format "esmodule" cannot be used in the "main" target without a .mjs extension or "type": "module" field.`,
origin: '@parcel/core',
codeFrames: [
{
language: 'json',
filePath: pkgFilePath ?? undefined,
code: contents,
codeHighlights: generateJSONCodeHighlights(contents, [
{
key: `/targets/${targetName}/outputFormat`,
type: 'value',
message: 'Declared output format defined here',
},
{
key: '/main',
type: 'value',View on GitHub (pinned to 59484858a1)
Solutions
- Add `"type": "module"` to package.json so Node treats .js as ESM.
- Change the `main` distEntry to a `.mjs` extension.
- Move ESM output to the `module` target instead of `main`.
- Switch the outputFormat back to `commonjs` if ESM is not required.
Example fix
// before (package.json)
{
"main": "dist/index.js",
"targets": { "main": { "outputFormat": "esmodule" } }
}
// after
{
"type": "module",
"main": "dist/index.js",
"targets": { "main": { "outputFormat": "esmodule" } }
} Defensive patterns
Strategy: validation
Validate before calling
function assertMainEsmCompat(pkg) {
const isMainEsm = pkg.targets?.main?.outputFormat === 'esmodule'
|| (pkg.main && pkg.main.endsWith('.mjs'));
if (isMainEsm && pkg.type !== 'module' && !pkg.main?.endsWith('.mjs')) {
throw new Error('Add "type":"module" or use .mjs for the main target ESM output.');
}
} Type guard
function mainEsmIsWellFormed(pkg) {
const of = pkg.targets?.main?.outputFormat;
if (of !== 'esmodule') return true;
return pkg.type === 'module' || (typeof pkg.main === 'string' && pkg.main.endsWith('.mjs'));
} Try / catch
try { await parcel.run(); } catch (e) {
if (/cannot be used in the "main" target/.test(e.message)) {
console.error('Add "type":"module" to package.json or rename main output to .mjs.');
} else throw e;
} Prevention
- When emitting ESM from `main`, set `"type":"module"` or use `.mjs`.
- Prefer the `module` target for ESM output.
- Audit package.json `type`/extension alignment during ESM migrations.
When it happens
Trigger: Configuring `targets.main.outputFormat = "esmodule"` (or relying on main with .js) while package.json lacks `"type": "module"` and the main distEntry is not `.mjs`.
Common situations: Switching a CommonJS library to ESM output without updating package.json type field or extension; using `main` instead of `module` for ESM; partial ESM migration where the runtime loader would misinterpret the file.
Related errors
- Library targets are not supported in serve mode.
- Expected package.json file in ${rootDir}
- Unexpected output file type ${ext} in target "${targetName}"
- The "global" output format is not supported in the "${target
- Scope hoisting cannot be disabled for library targets.
AI-assisted analysis of parcel-bundler/parcel@59484858a1 (2026-08-13).
Data as JSON: /api/errors/2fc304de6bafbcc3.
Report an issue: GitHub.