{"record":{"id":"77c18f7a54a8cfac","repo":"affaan-m/ECC","slug":"source-uninstall-failed","errorCode":"SOURCE_UNINSTALL_FAILED","errorMessage":"The destination is installed, but Claude could not remove the ${migration.sourceScope} source scope.","messagePattern":"The destination is installed, but Claude could not remove the (.+?) source scope\\.","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/claude-scope-migration.js","lineNumber":223,"sourceCode":"      const plugins = readPluginInventory(\n        run,\n        paths.projectRoot,\n        'source-uninstall-verification'\n      );\n      assertNoConflictingEccPlugins(plugins);\n      const installed = currentEccPlugins(plugins);\n      observedScopes = installed.map(plugin => plugin.scope);\n      if (\n        installed.length === 1\n        && installed[0].scope === destinationScope\n        && installed[0].enabled === true\n      ) {\n        return ['Claude reported an uninstall error, but destination-only state was verified.'];\n      }\n    } catch {\n      // Preserve the safest known two-scope state in the structured recovery.\n    }\n    throw migrationError(\n      'SOURCE_UNINSTALL_FAILED',\n      `The destination is installed, but Claude could not remove the ${migration.sourceScope} source scope.`,\n      {\n        phase: 'source-uninstall',\n        observedScopes,\n        recovery: recoveryCommands(migration.sourceScope, destinationScope),\n      }\n    );\n  }\n}\n\nfunction verifyFinalState(run, paths, destinationScope) {\n  const plugins = readPluginInventory(run, paths.projectRoot, 'final-verification');\n  return validateExpectedScopes(plugins, [destinationScope], {\n    code: 'FINAL_VERIFICATION_FAILED',\n    destinationScope,\n    message: `Could not verify destination-only ${CURRENT_PLUGIN_ID} state after source cleanup.`,\n    phase: 'final-verification',","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/claude-scope-migration.js#L205-L241","documentation":"ClaudeSetupError with code SOURCE_UNINSTALL_FAILED thrown by uninstallSource in scripts/lib/claude-scope-migration.js:223. During a two-scope ('resume') migration, the destination is already installed, but Claude must remove the old source-scope copy. If `claude plugin uninstall` fails or the post-uninstall inventory re-verification throws, the migrator raises this error describing the safe state (destination installed, source still present) and includes recovery commands for the source scope. The code path intentionally preserves the two-scope state so a retry can resume rather than leaving the user with nothing installed.","triggerScenarios":"Running a scope migration in resume mode where `claude plugin uninstall <id> --scope <sourceScope>` returns an error (non-zero exit, stderr output) or throws, and re-checking the plugin list still shows the source-scope installation alongside the destination.","commonSituations":"The plugin is locked because a Claude session has it loaded; filesystem permission problems in Claude's plugin cache directory; a stale/corrupt plugin registry entry that uninstall cannot remove; running uninstall while another Claude process holds the config.","solutions":["Retry the removal manually as the error suggests: `claude plugin uninstall <plugin-id> --scope <sourceScope> --keep-data`, then re-run the migration.","Close any running Claude Code sessions that may hold the plugin, then retry the uninstall.","Check write permissions on Claude's config/plugin directories for the source scope and fix them if uninstall failed on I/O.","If Claude reports an uninstall error but the destination-only state is actually verified (per the code), re-run the migration — it re-checks the inventory and may proceed as already-migrated."],"exampleFix":"// before: migration fails removing source scope 'user'\n\n// after\n// claude plugin uninstall everything-claude-code --scope user --keep-data\n// ecc migrate-scope --scope project  # resumes and completes","handlingStrategy":"retry","validationCode":"// Pre-flight: ensure no other Claude process holds the plugin and source scope is removable\nconst plugins = parsePluginList(run(['plugin', 'list', '--json'], { cwd }).stdout);\nif (plugins.some(p => p.scope === sourceScope)) {\n  run(['plugin', 'uninstall', CURRENT_PLUGIN_ID, '--scope', sourceScope, '--keep-data'], { cwd });\n}","typeGuard":"function isSourceRemoved(plugins, sourceScope) { return !plugins.some(p => p.scope === sourceScope); }","tryCatchPattern":"try { await migrateScope({ scope }) }\ncatch (e) {\n  if (e.code === 'SOURCE_UNINSTALL_FAILED') {\n    // safe two-scope state preserved; retry after manual uninstall\n    console.log('Run:', `claude plugin uninstall ${CURRENT_PLUGIN_ID} --scope <source> --keep-data`, 'then retry');\n  } else throw e;\n}","preventionTips":["Close running Claude Code sessions before migrating scopes.","Verify write permissions on Claude config/plugin directories for both scopes.","Run the uninstall manually first if the source scope is known to be problematic.","Never delete config directories mid-migration; let the tool preserve resumable state."],"tags":["claude-plugin","scope-migration","uninstall-failed","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"}