{"record":{"id":"660c8ee06d4f70ba","repo":"can1357/oh-my-pi","slug":"plugin-pluginid-is-installed-in-both-user-and-660c8e","errorCode":null,"errorMessage":"Plugin \"${pluginId}\" is installed in both user and project scope. Use --scope user or --scope project to specify which to upgrade.","messagePattern":"Plugin \"(.+?)\" is installed in both user and project scope\\. Use --scope user or --scope project to specify which to upgrade\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts","lineNumber":698,"sourceCode":"\tasync upgradePlugin(pluginId: string, scope?: \"user\" | \"project\"): Promise<InstalledPluginEntry> {\n\t\tconst parsed = parsePluginId(pluginId);\n\t\tif (!parsed) {\n\t\t\tthrow new Error(`Invalid plugin ID: \"${pluginId}\". Expected \"name@marketplace\".`);\n\t\t}\n\n\t\tconst { userEntries, projectEntries } = await this.#findInBothRegistries(pluginId);\n\n\t\tconst inUser = userEntries && userEntries.length > 0;\n\t\tconst inProject = projectEntries && projectEntries.length > 0;\n\n\t\tif (!inUser && !inProject) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" is not installed`);\n\t\t}\n\n\t\tlet resolvedScope: \"user\" | \"project\";\n\t\tif (inUser && inProject) {\n\t\t\tif (!scope) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Plugin \"${pluginId}\" is installed in both user and project scope. Use --scope user or --scope project to specify which to upgrade.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tresolvedScope = scope;\n\t\t} else if (inProject) {\n\t\t\tif (scope === \"user\") throw new Error(`Plugin \"${pluginId}\" is not installed in user scope`);\n\t\t\tresolvedScope = \"project\";\n\t\t} else {\n\t\t\tif (scope === \"project\") throw new Error(`Plugin \"${pluginId}\" is not installed in project scope`);\n\t\t\tresolvedScope = \"user\";\n\t\t}\n\n\t\treturn this.installPlugin(parsed.name, parsed.marketplace, { force: true, scope: resolvedScope });\n\t}\n\n\t// Upgrade a plugin across all scopes where it is installed.\n\t// Returns one entry per scope upgraded (0–2 entries).\n\tasync upgradePluginAcrossScopes(pluginId: string): Promise<InstalledPluginEntry[]> {","sourceCodeStart":680,"sourceCodeEnd":716,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts#L680-L716","documentation":"When a plugin with the same name@marketplace id is installed in BOTH the user-scope and project-scope registries, upgradePlugin() cannot decide which copy to re-install, so it throws unless the caller explicitly passes scope. The error text mirrors the CLI's --scope flag, telling you how to disambiguate.","triggerScenarios":"Calling upgradePlugin(id) with no second argument while an entry exists in both userEntries and projectEntries (both arrays non-empty). This is exactly the ambiguity case: inUser && inProject && !scope.","commonSituations":"A plugin was installed globally for personal use and later also added to a project's plugin registry; both copies linger after a scope migration; scripts call the API without the scope parameter that a human would pass as --scope on the CLI.","solutions":["Pass the desired scope: upgradePlugin(id, \"user\") or upgradePlugin(id, \"project\").","Decide which copy you actually want to upgrade — usually the project one for repo-pinned plugins.","If only one copy should exist, uninstall the redundant scope entry, then upgrade the remaining one.","In CLI usage, add --scope user or --scope project to the upgrade command."],"exampleFix":"// before\nawait manager.upgradePlugin(\"my-plugin@official\"); // installed in both scopes\n// after\nawait manager.upgradePlugin(\"my-plugin@official\", \"project\");","handlingStrategy":"validation","validationCode":"const { userEntries, projectEntries } = /* look up both scopes */;\nconst inUser = !!userEntries?.length, inProject = !!projectEntries?.length;\nconst scopeArg = inUser && inProject ? (scope ?? \"project\") : scope; // disambiguate up front\nawait manager.upgradePlugin(pluginId, scopeArg);","typeGuard":"null","tryCatchPattern":"try {\n  await manager.upgradePlugin(pluginId, scope);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"installed in both user and project scope\")) {\n    await manager.upgradePlugin(pluginId, \"project\"); // pick a deterministic default\n  } else throw err;\n}","preventionTips":["Always pass an explicit scope when both scopes may hold the same plugin.","Avoid installing the same plugin in both user and project registries; pick one owner scope.","In CLI wrappers, surface the --scope flag instead of hard-coding absence.","Detect dual installation before upgrading and prune the redundant entry."],"tags":["ambiguity","plugins","scope"],"backgroundTag":"ambiguous-plugin-scope","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}