{"record":{"id":"0b1ac879e5adf41b","repo":"can1357/oh-my-pi","slug":"invalid-plugin-id-pluginid-expected-name-m","errorCode":null,"errorMessage":"Invalid plugin ID: \"${pluginId}\". Expected \"name@marketplace\".","messagePattern":"Invalid plugin ID: \"(.+?)\"\\. Expected \"name@marketplace\"\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts","lineNumber":683,"sourceCode":"\t\t\t\t\tisNewer = Bun.semver.order(catalogVersion, installed.version) > 0;\n\t\t\t\t} catch {\n\t\t\t\t\tisNewer = catalogVersion !== installed.version;\n\t\t\t\t}\n\n\t\t\t\tif (isNewer) {\n\t\t\t\t\tupdates.push({ pluginId, scope, from: installed.version, to: catalogVersion });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn updates;\n\t}\n\n\t// Re-install a specific plugin at the latest catalog version (force-overwrites).\n\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}","sourceCodeStart":665,"sourceCodeEnd":701,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts#L665-L701","documentation":"MarketplaceManager.upgradePlugin() requires a fully qualified plugin ID of the form \"name@marketplace\". Before doing any registry lookup it runs parsePluginId(), and if the ID cannot be split into a plugin name and a marketplace name it throws this error immediately. This is a fail-fast input validation so the upgrade can unambiguously locate the plugin in a specific marketplace.","triggerScenarios":"Calling upgradePlugin(pluginId) with a bare plugin name (\"my-plugin\"), an empty string, a malformed ID like \"name@\" or \"@marketplace\", or any string that parsePluginId() cannot split on a valid '@' separator.","commonSituations":"Users copy a plugin name from a list without its marketplace suffix; scripts pass a CLI argument that was only the plugin name; a shell script strips the @marketplace portion during quoting/escaping; or the ID comes from a stale config where the marketplace was removed.","solutions":["Include the marketplace in the ID: call upgradePlugin(\"my-plugin@official\") instead of upgradePlugin(\"my-plugin\").","Check the installed plugin entry (e.g. from listInstalledPlugins()) and use its exact id field, which is already in name@marketplace form.","If the ID is user-supplied, validate it matches /[^@]+@[^@]+/ before calling and prompt or error with a helpful message.","If the marketplace name is unknown, list available marketplaces first and pick the one the plugin was installed from."],"exampleFix":"// before\nawait manager.upgradePlugin(\"my-plugin\");\n// after\nawait manager.upgradePlugin(\"my-plugin@official\");","handlingStrategy":"validation","validationCode":"function isValidPluginId(id: string): boolean {\n  const at = id.indexOf(\"@\");\n  return at > 0 && at < id.length - 1 && !id.slice(at + 1).includes(\"@\");\n}\nif (!isValidPluginId(pluginId)) throw new Error(`Plugin ID must be \"name@marketplace\", got: \"${pluginId}\"`);\nawait manager.upgradePlugin(pluginId, scope);","typeGuard":"function isPluginId(value: string): value is `${string}@${string}` {\n  const at = value.indexOf(\"@\");\n  return at > 0 && at < value.length - 1;\n}","tryCatchPattern":"try {\n  await manager.upgradePlugin(pluginId, scope);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Invalid plugin ID:\")) {\n    console.error(`Bad plugin id \"${pluginId}\" — use name@marketplace form.`);\n  } else throw err;\n}","preventionTips":["Always build plugin IDs as `${name}@${marketplace}` from structured data, never by hand.","Take the id verbatim from a listing API's entry.id instead of retyping it.","Validate the name@marketplace pattern in CLI argument parsing before reaching the manager.","Keep marketplace names in a config constant to avoid typos in the @marketplace segment."],"tags":["validation","plugins","argument-format"],"backgroundTag":"invalid-plugin-id","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}