jackwener/OpenCLI · error · PluginError

Plugin "${pluginName}" is already installed at ${targetDir}

Error message

Plugin "${pluginName}" is already installed at ${targetDir}

What it means

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.

Source

Thrown at src/plugin.ts:747

    }

    // Single plugin mode
    return installSinglePlugin(tmpCloneDir, parsed.cloneUrl!, repoName, manifest);
  });
}

/** Install a single (non-monorepo) plugin. */
function installSinglePlugin(
  cloneDir: string,
  cloneUrl: string,
  name: string,
  manifest: PluginManifest | null,
): string {
  const pluginName = manifest?.name ?? name;
  const targetDir = path.join(PLUGINS_DIR, pluginName);

  if (fs.existsSync(targetDir)) {
    throw new PluginError(`Plugin "${pluginName}" is already installed at ${targetDir}`, 'Use "opencli plugin uninstall" first, or pick a different name.');
  }

  ensureStandalonePluginReady(cloneDir);
  publishStandalonePlugin(cloneDir, targetDir, (commitHash) => {
    const lock = readLockFile();
    if (commitHash) {
      upsertLockEntry(lock, pluginName, {
        source: { kind: 'git', url: cloneUrl },
        commitHash,
      });
      writeLockFile(lock);
    }
  });

  return pluginName;
}

/**

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Uninstall the existing plugin first: `opencli plugin uninstall <name>`.
  2. If you want the newer version of the same plugin, use `opencli plugin update <name>` instead of install.
  3. If the existing directory is a stale leftover, remove it: `rm -rf ~/.opencli/plugins/<name>`.
  4. If two distinct plugins collide, change the `name` in one plugin's opencli-plugin.json.
  5. Check `opencli plugin list` to see what is installed before installing.

Example fix

// before
opencli plugin install github:user/repo   // already installed
// after
opencli plugin update repo
// or
opencli plugin uninstall repo && opencli plugin install github:user/repo
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from 'node:fs';
import * as path from 'node:path';
const pluginsDir = path.join(home, '.opencli', 'plugins');
const name = 'repo'; // manifest name or repo basename
if (fs.existsSync(path.join(pluginsDir, name))) {
  // already installed: call update/uninstall instead of install
}

Try / catch

try {
  installPlugin(source);
} catch (err) {
  if (err instanceof PluginError && err.message.includes('is already installed')) {
    const name = err.message.match(/Plugin "([^"]+)"/)?.[1];
    uninstallPlugin(name); // or updatePlugin(name); then retry install if needed
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/27d1c99ec97abac9. Report an issue: GitHub.