{"record":{"id":"9617625772afb289","repo":"affaan-m/ECC","slug":"ambiguous-plugin-scopes","errorCode":"AMBIGUOUS_PLUGIN_SCOPES","errorMessage":"Cannot safely migrate ${CURRENT_PLUGIN_ID} from ambiguous scopes: ${observedScopes.join(', ')}.","messagePattern":"Cannot safely migrate (.+?) from ambiguous scopes: (.+?)\\.","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/claude-scope-migration.js","lineNumber":83,"sourceCode":"      'PLUGIN_NOT_INSTALLED',\n      `${CURRENT_PLUGIN_ID} is not installed, so there is no source scope to migrate.`,\n      {\n        observedScopes,\n        recovery: [\n          `ecc setup --mode claude-plugin --scope ${destinationScope} --yes`,\n        ],\n      }\n    );\n  }\n  if (\n    installed.length > 2\n    || uniqueScopes.size !== installed.length\n    || (\n      installed.length === 2\n      && !uniqueScopes.has(destinationScope)\n    )\n  ) {\n    throw migrationError(\n      'AMBIGUOUS_PLUGIN_SCOPES',\n      `Cannot safely migrate ${CURRENT_PLUGIN_ID} from ambiguous scopes: ${observedScopes.join(', ')}.`,\n      { observedScopes }\n    );\n  }\n\n  if (installed.length === 1 && installed[0].scope === destinationScope) {\n    if (installed[0].enabled !== true) {\n      throw migrationError(\n        'DESTINATION_VERIFICATION_FAILED',\n        `${CURRENT_PLUGIN_ID} exists at ${destinationScope} scope but is not enabled.`,\n        {\n          phase: 'destination-verification',\n          observedScopes,\n          recovery: recoveryCommands(null, destinationScope),\n        }\n      );\n    }","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/claude-scope-migration.js#L65-L101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the inventory: `claude plugin list --json` and note every scope where the ECC plugin appears.","Manually uninstall extras, keeping only the copy you want as the source: `claude plugin uninstall <plugin-id> --scope <scope> --keep-data`.","Then re-run the migration with the single remaining source and the desired destination scope.","If duplicates are stale registry entries rather than real installs, reinstall cleanly: uninstall everywhere, then `ecc setup --mode claude-plugin --scope <destination> --yes`."],"exampleFix":"// before: plugin at user, project AND local scopes → ambiguous\n// claude plugin uninstall everything-claude-code --scope local --keep-data\n// claude plugin uninstall everything-claude-code --scope project --keep-data\n\n// after: single source scope remains, migration proceeds\n// ecc migrate-scope --scope user","handlingStrategy":"validation","validationCode":"const plugins = parsePluginList(run(['plugin', 'list', '--json'], { cwd }).stdout)\n  .filter(p => p.id === CURRENT_PLUGIN_ID);\nconst scopes = plugins.map(p => p.scope);\nconst unique = new Set(scopes);\nconst ambiguous = plugins.length > 2 || unique.size !== plugins.length ||\n  (plugins.length === 2 && !unique.has(destinationScope));\nif (ambiguous) throw new Error(`Resolve duplicate scopes first: ${scopes.join(', ')}`);","typeGuard":"function hasUnambiguousInventory(plugins, dest) {\n  const scopes = plugins.map(p => p.scope);\n  const u = new Set(scopes);\n  return plugins.length >= 1 && plugins.length <= 2 && u.size === plugins.length &&\n    (plugins.length !== 2 || u.has(dest));\n}","tryCatchPattern":"try { await migrateScope({ scope }) }\ncatch (e) {\n  if (e.code === 'AMBIGUOUS_PLUGIN_SCOPES') {\n    for (const s of e.details.observedScopes) console.log('Uninstall extra copy at scope:', s);\n  } else throw e;\n}","preventionTips":["Install the plugin at exactly one scope; avoid re-running setup with different --scope values without cleaning up.","After any failed migration, run `claude plugin list --json` and remove duplicates before retrying.","Script installs so scope changes uninstall the previous scope first.","Audit plugin inventory periodically in CI or setup scripts."],"tags":["claude-plugin","scope-migration","ambiguous-state","cli"],"backgroundTag":"invalid-state-transition","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}