{"record":{"id":"e743fdf6afe0b9f1","repo":"can1357/oh-my-pi","slug":"plugin-id-not-found-in-registry","errorCode":null,"errorMessage":"Plugin \"${id}\" not found in registry","messagePattern":"Plugin \"(.+?)\" not found in registry","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/registry.ts","lineNumber":169,"sourceCode":"}\n\n// ── Installed plugin CRUD ────────────────────────────────────────────\n\nexport function addInstalledPlugin(\n\treg: InstalledPluginsRegistry,\n\tid: string,\n\tentry: InstalledPluginEntry,\n): InstalledPluginsRegistry {\n\tconst existing = reg.plugins[id] ?? [];\n\treturn {\n\t\t...reg,\n\t\tplugins: { ...reg.plugins, [id]: [...existing, entry] },\n\t};\n}\n\nexport function removeInstalledPlugin(reg: InstalledPluginsRegistry, id: string): InstalledPluginsRegistry {\n\tif (!(id in reg.plugins)) {\n\t\tthrow new Error(`Plugin \"${id}\" not found in registry`);\n\t}\n\tconst { [id]: _, ...rest } = reg.plugins;\n\treturn { ...reg, plugins: rest };\n}\n\nexport function getInstalledPlugin(reg: InstalledPluginsRegistry, id: string): InstalledPluginEntry[] | undefined {\n\treturn reg.plugins[id];\n}\n\n/**\n * Collect all installPath values referenced by any of the provided registries.\n * Use this before deleting a cached plugin directory to verify it is not still\n * referenced by another scope's registry.\n */\nexport function collectReferencedPaths(...registries: InstalledPluginsRegistry[]): Set<string> {\n\treturn new Set(\n\t\tregistries.flatMap(r =>\n\t\t\tObject.values(r.plugins)","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/registry.ts#L151-L187","documentation":"removeInstalledPlugin() deletes the plugin's entry list from the InstalledPluginsRegistry keyed by full plugin id (\"name@marketplace\"). If the id is not a key in reg.plugins, it throws rather than returning the registry unchanged, so callers know the uninstall targeted nothing.","triggerScenarios":"Calling removeInstalledPlugin(reg, id) for a plugin never installed, already uninstalled, or under an id that differs from the stored key (missing @marketplace suffix, different marketplace name, case mismatch).","commonSituations":"Double-uninstall from retrying a failed script; uninstalling in a project where only the user-scope registry has the plugin (or vice versa); constructing the id by hand without the @marketplace part while the registry keys are fully qualified.","solutions":["Confirm the exact key with getInstalledPlugin(reg, id) before removing","Use the fully qualified id \"name@marketplace\" matching the registry key","Make uninstall idempotent: check `id in reg.plugins` first and skip when absent","Reinstall if it was already removed and uninstall is genuinely needed again"],"exampleFix":"// before\nreg = removeInstalledPlugin(reg, \"myplugin\"); // throws — key is \"myplugin@community\"\n// after\nif (getInstalledPlugin(reg, \"myplugin@community\")) {\n  reg = removeInstalledPlugin(reg, \"myplugin@community\");\n}","handlingStrategy":"validation","validationCode":"import { getInstalledPlugin, removeInstalledPlugin } from './registry';\nconst id = `${name}@${marketplace}`;\nif (getInstalledPlugin(reg, id)) {\n  reg = removeInstalledPlugin(reg, id);\n}","typeGuard":"null","tryCatchPattern":"try {\n  reg = removeInstalledPlugin(reg, id);\n} catch (err) {\n  if (err.message === `Plugin \"${id}\" not found in registry`) {\n    // already uninstalled — idempotent no-op\n  } else throw err;\n}","preventionTips":["Key uninstall operations on the full \"name@marketplace\" id as stored in reg.plugins","Check getInstalledPlugin before removing to keep scripts idempotent","Load the correct registry (user vs project) for the scope you intend","Avoid hand-building ids — reuse values read from the registry"],"tags":["plugin","not-found","registry"],"backgroundTag":"plugin-not-in-registry","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}