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

  1. Add `"type": "module"` to package.json so Node treats .js as ESM.
  2. Change the `main` distEntry to a `.mjs` extension.
  3. Move ESM output to the `module` target instead of `main`.
  4. 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 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


AI-assisted analysis of parcel-bundler/parcel@59484858a1 (2026-08-13). Data as JSON: /api/errors/2fc304de6bafbcc3. Report an issue: GitHub.