angular/angular-cli · error · Error

Multiple bundles have been named the same: '${duplicates.joi

Error message

Multiple bundles have been named the same: '${duplicates.join(`', '`)}'.

What it means

generateEntryPoints assembles the initial webpack entry-point list (runtime, polyfills, styles, scripts, vendor, main) from injectable styles/scripts in the build options. If any bundle name collides, the duplicates check fires this error so the produced HTML does not contain ambiguous, overwriting script tags. In the checked-in code the duplicate scan only compares against the first entry (runtime) via entryPoints[0], so it only reliably catches a name colliding with 'runtime', though the intent is to reject any duplicate bundle name.

Source

Thrown at packages/angular_devkit/build_angular/src/utils/package-chunk-sort.ts:46

    // remove duplicates
    return [...new Set(entryPoints)].map<EntryPointsType>((f) => [f, false]);
  };

  const entryPoints: EntryPointsType[] = [
    ['runtime', !options.isHMREnabled],
    ['polyfills', true],
    ...extraEntryPoints(options.styles, 'styles'),
    ...extraEntryPoints(options.scripts, 'scripts'),
    ['vendor', true],
    ['main', true],
  ];

  const duplicates = entryPoints.filter(
    ([name]) => entryPoints[0].indexOf(name) !== entryPoints[0].lastIndexOf(name),
  );

  if (duplicates.length > 0) {
    throw new Error(`Multiple bundles have been named the same: '${duplicates.join(`', '`)}'.`);
  }

  return entryPoints;
}

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Set an explicit unique "bundleName" on the conflicting scripts/styles entry in angular.json.
  2. Rename the source file so its basename does not collide with reserved bundle names (main, runtime, polyfills, styles, scripts, vendor).
  3. If two extra entries collide with each other, merge them or give each a distinct bundleName.
  4. Verify inject: false for entries that should not participate in the initial entry list.

Example fix

// before (angular.json)
"scripts": [{ "input": "node_modules/some-lib/main.js", "inject": true }]
// after
"scripts": [{ "input": "node_modules/some-lib/main.js", "inject": true, "bundleName": "some-lib" }]
Defensive patterns

Strategy: validation

Validate before calling

// Detect colliding bundle names before building
const RESERVED = ['runtime', 'polyfills', 'styles', 'scripts', 'vendor', 'main'];
function findBundleNameCollisions(scripts = [], styles = []) {
  const names = [];
  for (const e of [...scripts, ...styles]) {
    if (e.inject === false) continue;
    const name = e.bundleName ??
      require('path').basename(e.input.split('*').pop().replace(/\.[jt]sx?$/, '').replace(/\.(css|scss|sass|less|styl)$/, ''), '.js');
    names.push(name);
  }
  return [...new Set(names.filter(n => RESERVED.includes(n) || names.indexOf(n) !== names.lastIndexOf(n)))];
}

Try / catch

try {
  const entryPoints = generateEntryPoints(options);
  return entryPoints;
} catch (err) {
  if (/^Multiple bundles have been named the same:/.test(err?.message ?? '')) {
    console.error('Assign a unique "bundleName" to the conflicting styles/scripts entry in angular.json.');
  } else throw err;
}

Prevention

When it happens

Trigger: Building with angular.json styles or scripts entries whose computed bundleName (glob basename, or explicit bundleName via normalizeExtraEntryPoints) collides with a reserved entry name — 'runtime', 'polyfills', 'styles', 'scripts', 'vendor', 'main', or another extra entry — and inject: true.

Common situations: Adding a script like node_modules/x/main.js (bundleName 'main') or a style named 'styles.css' from a nonstandard path, after upgrades where default bundle naming changed (e.g. extra entries now default to 'scripts'/'styles'), or two extra entries resolving to the same bundleName.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/bb5af9924fdc6cdd. Report an issue: GitHub.