parcel-bundler/parcel · error · ThrowableDiagnostic

External modules are not supported when building for browser

Error message

External modules are not supported when building for browser

What it means

A `ThrowableDiagnostic` thrown by `ScopeHoistingPackager.addExternal` when a dependency is marked external but the bundle's output format is `global` (the default for browser builds). External modules rely on a host module system (CommonJS/ESM), which a `global`/IIFE browser bundle has no way to import — so Parcel refuses rather than emit broken output. The diagnostic includes a code frame at the dependency's source location.

Source

Thrown at packages/packagers/js/src/ScopeHoistingPackager.js:818

    if (
      this.wrappedAssets.has(asset.id) ||
      (this.bundle.env.outputFormat === 'commonjs' &&
        asset === this.bundle.getMainEntry())
    ) {
      let exportsName = asset.symbols.get('*')?.local || `$${assetId}$exports`;
      replacements.set(exportsName, 'module.exports');
    }

    return [depMap, replacements];
  }

  addExternal(
    dep: Dependency,
    replacements?: Map<string, string>,
    referencedBundle?: NamedBundle,
  ) {
    if (this.bundle.env.outputFormat === 'global') {
      throw new ThrowableDiagnostic({
        diagnostic: {
          message:
            'External modules are not supported when building for browser',
          codeFrames: [
            {
              filePath: nullthrows(dep.sourcePath),
              codeHighlights: dep.loc
                ? [convertSourceLocationToHighlight(dep.loc)]
                : [],
            },
          ],
        },
      });
    }

    let specifier = dep.specifier;
    if (referencedBundle) {
      specifier = relativeBundlePath(this.bundle, referencedBundle);

View on GitHub (pinned to 59484858a1)

Solutions

  1. Change the bundle's output format to `esmodule` or `commonjs` so externals can be resolved by the host (set in `.parcelrc`/target config or `outputFormat`).
  2. Remove the `externals` entry for the browser/global target.
  3. Bundle the dependency instead of marking it external for global output.
  4. If the global is provided by a script tag, ensure the target is configured for global-output + matching `globals`, or switch formats.

Example fix

// before — package.json
{
  "targets": { "browser": { "source": "src/index.js" } },
  "externals": { "react": "React" }
}
// after — use esmodule output for externals
{
  "targets": { "browser": { "source": "src/index.js", "outputFormat": "esmodule" } },
  "externals": { "react": "React" }
}
Defensive patterns

Strategy: validation

Validate before calling

function assertNoExternalsForGlobal(env, externals) {
  if (env.outputFormat === 'global' && externals && externals.size > 0) {
    throw new Error('Externals require esmodule/commonjs output, not global');
  }
}

Type guard

function isGlobalOutput(env) { return env?.outputFormat === 'global'; }

Try / catch

try { packager.package(...); }
catch (e) {
  if (/External modules are not supported when building for browser/.test(e.message)) {
    // switch target.outputFormat to 'esmodule' or remove externals
  } else throw e;
}

Prevention

When it happens

Trigger: Configuring `externals` (or a package marked external) while targeting a browser global output; setting `--target browser` (global format) with `externals` in package.json; depending on a peer/global that Parcel treats as external under global format.

Common situations: Library build with `externals` to exclude a peer dep, but target default resolves to global; migrating a config from esbuild/webpack `external` without switching output format; splitting config per target and forgetting to clear externals for the browser target.

Related errors


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