{"record":{"id":"27d1c99ec97abac9","repo":"jackwener/OpenCLI","slug":"plugin-pluginname-is-already-installed-at-t","errorCode":null,"errorMessage":"Plugin \"${pluginName}\" is already installed at ${targetDir}","messagePattern":"Plugin \"(.+?)\" is already installed at (.+?)","errorType":"validation","errorClass":"PluginError","httpStatus":null,"severity":"error","filePath":"src/plugin.ts","lineNumber":747,"sourceCode":"    }\n\n    // Single plugin mode\n    return installSinglePlugin(tmpCloneDir, parsed.cloneUrl!, repoName, manifest);\n  });\n}\n\n/** Install a single (non-monorepo) plugin. */\nfunction installSinglePlugin(\n  cloneDir: string,\n  cloneUrl: string,\n  name: string,\n  manifest: PluginManifest | null,\n): string {\n  const pluginName = manifest?.name ?? name;\n  const targetDir = path.join(PLUGINS_DIR, pluginName);\n\n  if (fs.existsSync(targetDir)) {\n    throw new PluginError(`Plugin \"${pluginName}\" is already installed at ${targetDir}`, 'Use \"opencli plugin uninstall\" first, or pick a different name.');\n  }\n\n  ensureStandalonePluginReady(cloneDir);\n  publishStandalonePlugin(cloneDir, targetDir, (commitHash) => {\n    const lock = readLockFile();\n    if (commitHash) {\n      upsertLockEntry(lock, pluginName, {\n        source: { kind: 'git', url: cloneUrl },\n        commitHash,\n      });\n      writeLockFile(lock);\n    }\n  });\n\n  return pluginName;\n}\n\n/**","sourceCodeStart":729,"sourceCodeEnd":765,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/plugin.ts#L729-L765","documentation":"installSinglePlugin computes the target directory ~/.opencli/plugins/<pluginName> (name from the manifest or the source repo name) and throws a PluginError if that directory already exists. opencli refuses to silently overwrite an existing installation, telling the user to uninstall first or choose a different name.","triggerScenarios":"installPlugin('github:user/repo') when a plugin with the same name (manifest `name` field or repo basename) is already installed — whether from the same repo (double install), a different repo that claims the same manifest name, or a leftover directory in the plugins folder.","commonSituations":"Re-running the same install command instead of `plugin update`; two unrelated plugins whose manifests both set name to \"utils\"; plugins dir contains manual copies; previous install crashed after publish but before lock update.","solutions":["Uninstall the existing plugin first: `opencli plugin uninstall <name>`.","If you want the newer version of the same plugin, use `opencli plugin update <name>` instead of install.","If the existing directory is a stale leftover, remove it: `rm -rf ~/.opencli/plugins/<name>`.","If two distinct plugins collide, change the `name` in one plugin's opencli-plugin.json.","Check `opencli plugin list` to see what is installed before installing."],"exampleFix":"// before\nopencli plugin install github:user/repo   // already installed\n// after\nopencli plugin update repo\n// or\nopencli plugin uninstall repo && opencli plugin install github:user/repo","handlingStrategy":"validation","validationCode":"import * as fs from 'node:fs';\nimport * as path from 'node:path';\nconst pluginsDir = path.join(home, '.opencli', 'plugins');\nconst name = 'repo'; // manifest name or repo basename\nif (fs.existsSync(path.join(pluginsDir, name))) {\n  // already installed: call update/uninstall instead of install\n}","typeGuard":null,"tryCatchPattern":"try {\n  installPlugin(source);\n} catch (err) {\n  if (err instanceof PluginError && err.message.includes('is already installed')) {\n    const name = err.message.match(/Plugin \"([^\"]+)\"/)?.[1];\n    uninstallPlugin(name); // or updatePlugin(name); then retry install if needed\n  } else throw err;\n}","preventionTips":["Check `opencli plugin list` before installing.","Use `plugin update` for refreshing an existing plugin instead of `plugin install`.","Ensure each plugin's manifest `name` is unique across your installed set.","Clean stale leftovers in ~/.opencli/plugins rather than installing over them."],"tags":["duplicate-install","plugin-install","name-collision"],"backgroundTag":"plugin-already-installed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}