can1357/oh-my-pi · error · Error
Invalid plugin ID format: "${pluginId}". Expected "name@mark
Error message
Invalid plugin ID format: "${pluginId}". Expected "name@marketplace". What it means
uninstallPlugin requires the plugin to be identified as "name@marketplace". It runs parsePluginId(pluginId) and throws when the string does not match that format. This prevents ambiguous deletions where a bare name could match plugins from multiple marketplaces.
Source
Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts:449
}
} catch {
// Missing or invalid — try next
}
}
// 3. Git SHA from source definition
if (typeof entry.source === "object" && "sha" in entry.source && entry.source.sha) {
return entry.source.sha.slice(0, 7);
}
return "0.0.0";
}
/** Validates and removes a marketplace plugin, or only validates when `dryRun` is set. */
async uninstallPlugin(pluginId: string, scope?: "user" | "project", options?: { dryRun?: boolean }): Promise<void> {
const parsed = parsePluginId(pluginId);
if (!parsed) {
throw new Error(`Invalid plugin ID format: "${pluginId}". Expected "name@marketplace".`);
}
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 remove.`,
);
}View on GitHub (pinned to 9690622007)
Solutions
- Pass the full id as "name@marketplace", e.g. uninstallPlugin("formatter@community")
- Look up the exact installed id in the installed-plugins registry (or via list) and copy it verbatim
- If building the id programmatically, use buildPluginId(name, marketplace)
Example fix
// before
await manager.uninstallPlugin("formatter");
// after
await manager.uninstallPlugin("formatter@community"); Defensive patterns
Strategy: validation
Validate before calling
function isValidPluginId(id: string): boolean {
const at = id.lastIndexOf("@");
return at > 0 && at < id.length - 1 && !id.slice(0, at).includes("@") === false || at > 0; // keep simple:
}
// simpler:
if (!/^[^@\s]+@[^@\s]+$/.test(pluginId)) throw new Error(`need name@marketplace, got ${pluginId}`);
await manager.uninstallPlugin(pluginId); Type guard
function isPluginId(v: string): boolean {
const m = /^([^@\s]+)@([^@\s]+)$/.exec(v);
return !!m;
} Try / catch
try {
await manager.uninstallPlugin(rawArg);
} catch (err) {
if (err instanceof Error && err.message.includes("Invalid plugin ID format")) {
console.error(`Usage: omp plugin uninstall <name>@<marketplace> (got "${rawArg}")`);
} else throw err;
} Prevention
- Always build ids with buildPluginId(name, marketplace) instead of string concatenation
- Quote "name@marketplace" arguments in shell scripts so @ parsing quirks don't bite
- Validate user input against /^[^@]+@[^@]+$/ before calling uninstallPlugin
- Show id format in CLI help text and error hints
When it happens
Trigger: Calling uninstallPlugin with an id missing the @marketplace suffix, containing extra @ separators the parser rejects, or being empty/whitespace — e.g. uninstallPlugin("formatter") instead of uninstallPlugin("formatter@community").
Common situations: Users typing just the plugin name on the CLI; scripts passing a name copied from a display label rather than the registry id; shell quoting stripping an @ token; stale docs predating the name@marketplace convention.
Related errors
- unknown file type: {value}
- invalid size: {value}
- 2
- invalid --block-size argument '{0}'
- invalid --time-style argument {} Possible values are: - [p
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/286a0c90500b93f4.
Report an issue: GitHub.