affaan-m/ECC · error

AMBIGUOUS_PLUGIN_SCOPES

AMBIGUOUS_PLUGIN_SCOPES

Error message

Cannot safely migrate ${CURRENT_PLUGIN_ID} from ambiguous scopes: ${observedScopes.join(', ')}.

What it means

ClaudeSetupError with code AMBIGUOUS_PLUGIN_SCOPES thrown by assertMigrationInventory in scripts/lib/claude-scope-migration.js:83. The migrator can only proceed when the plugin inventory is unambiguous: at most 2 installations, all at distinct scopes, and if there are 2 they must include the destination scope (a resumable two-scope state). Any other combination — three or more copies, duplicate scopes, or two copies neither of which is at the destination — is unsafe to auto-repair, so the tool refuses with the observed scope list.

Solutions

  1. Inspect the inventory: `claude plugin list --json` and note every scope where the ECC plugin appears.
  2. Manually uninstall extras, keeping only the copy you want as the source: `claude plugin uninstall <plugin-id> --scope <scope> --keep-data`.
  3. Then re-run the migration with the single remaining source and the desired destination scope.
  4. If duplicates are stale registry entries rather than real installs, reinstall cleanly: uninstall everywhere, then `ecc setup --mode claude-plugin --scope <destination> --yes`.

Example fix

// before: plugin at user, project AND local scopes → ambiguous
// claude plugin uninstall everything-claude-code --scope local --keep-data
// claude plugin uninstall everything-claude-code --scope project --keep-data

// after: single source scope remains, migration proceeds
// ecc migrate-scope --scope user
Defensive patterns

Strategy: validation

Validate before calling

const plugins = parsePluginList(run(['plugin', 'list', '--json'], { cwd }).stdout)
  .filter(p => p.id === CURRENT_PLUGIN_ID);
const scopes = plugins.map(p => p.scope);
const unique = new Set(scopes);
const ambiguous = plugins.length > 2 || unique.size !== plugins.length ||
  (plugins.length === 2 && !unique.has(destinationScope));
if (ambiguous) throw new Error(`Resolve duplicate scopes first: ${scopes.join(', ')}`);

Type guard

function hasUnambiguousInventory(plugins, dest) {
  const scopes = plugins.map(p => p.scope);
  const u = new Set(scopes);
  return plugins.length >= 1 && plugins.length <= 2 && u.size === plugins.length &&
    (plugins.length !== 2 || u.has(dest));
}

Try / catch

try { await migrateScope({ scope }) }
catch (e) {
  if (e.code === 'AMBIGUOUS_PLUGIN_SCOPES') {
    for (const s of e.details.observedScopes) console.log('Uninstall extra copy at scope:', s);
  } else throw e;
}

Prevention

When it happens

Trigger: Running a scope migration when `claude plugin list --json` shows the ECC plugin installed in a state like [user, project, local] (3 copies), [user, user] (duplicate scope entries), or two installations at scopes that both differ from the requested --scope destination.

Common situations: The plugin was incrementally installed at multiple scopes over time (user install then project install then local install) and now three copies exist; a partial previous migration crashed leaving duplicates; Claude's plugin registry has stale duplicate entries from a re-install that did not clean up.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/9617625772afb289. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/claude-scope-migration.js:83

      'PLUGIN_NOT_INSTALLED',
      `${CURRENT_PLUGIN_ID} is not installed, so there is no source scope to migrate.`,
      {
        observedScopes,
        recovery: [
          `ecc setup --mode claude-plugin --scope ${destinationScope} --yes`,
        ],
      }
    );
  }
  if (
    installed.length > 2
    || uniqueScopes.size !== installed.length
    || (
      installed.length === 2
      && !uniqueScopes.has(destinationScope)
    )
  ) {
    throw migrationError(
      'AMBIGUOUS_PLUGIN_SCOPES',
      `Cannot safely migrate ${CURRENT_PLUGIN_ID} from ambiguous scopes: ${observedScopes.join(', ')}.`,
      { observedScopes }
    );
  }

  if (installed.length === 1 && installed[0].scope === destinationScope) {
    if (installed[0].enabled !== true) {
      throw migrationError(
        'DESTINATION_VERIFICATION_FAILED',
        `${CURRENT_PLUGIN_ID} exists at ${destinationScope} scope but is not enabled.`,
        {
          phase: 'destination-verification',
          observedScopes,
          recovery: recoveryCommands(null, destinationScope),
        }
      );
    }

View on GitHub (pinned to 8321021c54)