can1357/oh-my-pi · error · Error

Plugin "${pluginId}" is installed in both user and project s

Error message

Plugin "${pluginId}" is installed in both user and project scope. Use --scope user or --scope project to specify which to modify.

What it means

When a plugin id exists in BOTH the user-scope and project-scope installed-plugins registries, setPluginEnabled() refuses to guess which copy the caller means and throws this error, asking for an explicit scope. This avoids toggling enablement on the wrong installation — the project copy normally shadows the user copy, and the two have independent enabled flags.

Source

Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts:568

		}
		return results;
	}

	async setPluginEnabled(pluginId: string, enabled: boolean, scope?: "user" | "project"): Promise<void> {
		const { userEntries, projectEntries, userReg, projectReg } = await this.#findInBothRegistries(pluginId);

		const inUser = userEntries && userEntries.length > 0;
		const inProject = projectEntries && projectEntries.length > 0;

		if (!inUser && !inProject) {
			throw new Error(`Plugin "${pluginId}" is not installed`);
		}

		// Disambiguation: if installed in both scopes and no explicit scope, require one.
		let targetScope: "user" | "project";
		if (inUser && inProject) {
			if (!scope) {
				throw new Error(
					`Plugin "${pluginId}" is installed in both user and project scope. Use --scope user or --scope project to specify which to modify.`,
				);
			}
			targetScope = scope;
		} else if (inProject) {
			if (scope === "user") {
				throw new Error(`Plugin "${pluginId}" is not installed in user scope`);
			}
			targetScope = "project";
		} else {
			if (scope === "project") {
				throw new Error(`Plugin "${pluginId}" is not installed in project scope`);
			}
			targetScope = "user";
		}

		const reg = targetScope === "project" ? projectReg : userReg;
		const entries = targetScope === "project" ? projectEntries! : userEntries!;

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass an explicit scope: `--scope project` to modify the project-local copy (usually what shadows at runtime), or `--scope user` for the global one.
  2. In code, call setPluginEnabled(id, enabled, "project") or (id, enabled, "user") instead of omitting the third argument.
  3. If only one copy is needed, uninstall the redundant scope first so future scope-less calls resolve unambiguously.

Example fix

// before
await manager.setPluginEnabled("formatter@team", false);
// after
await manager.setPluginEnabled("formatter@team", false, "project");
Defensive patterns

Strategy: validation

Validate before calling

const installed = await manager.listInstalledPlugins();
const copies = installed.filter(p => p.id === pluginId);
if (copies.length > 1 && !scope) {
  scope = "project"; // project copy shadows user copy; pick explicitly
}

Type guard

function needsExplicitScope(copies: { scope: "user" | "project" }[]): boolean {
  return copies.some(c => c.scope === "user") && copies.some(c => c.scope === "project");
}

Try / catch

try {
  await manager.setPluginEnabled(pluginId, enabled);
} catch (err) {
  if (err instanceof Error && err.message.includes("installed in both user and project scope")) {
    await manager.setPluginEnabled(pluginId, enabled, "project");
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `setPluginEnabled(pluginId, enabled)` with the `scope` argument omitted (or the CLI omitting --scope) while `#findInBothRegistries(pluginId)` returns non-empty entries for both userEntries and projectEntries.

Common situations: A plugin was originally installed at user scope and later also installed into a project (or vice versa); a team repo committed a project-scope plugin registry for something the developer already had globally; scripted automation that never passes --scope starts failing after a second-scope install.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/63691c13f5010ce5. Report an issue: GitHub.