jackwener/OpenCLI · error · PluginError

Failed to clone plugin: ${getErrorMessage(err)}

Error message

Failed to clone plugin: ${getErrorMessage(err)}

What it means

cloneRepoToTemp runs `git clone --depth 1 <url>` into a temp dir via execFileSync. If git exits non-zero (bad URL, auth failure, network outage, missing git binary), the error is wrapped in a PluginError with the underlying git message. opencli throws this because a plugin cannot be installed or updated without cloning its source repository.

Source

Thrown at src/plugin.ts:239

function createSiblingTempPath(dest: string, kind: 'tmp' | 'bak'): string {
  const suffix = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
  return path.join(path.dirname(dest), `.${path.basename(dest)}.${kind}-${suffix}`);
}

function cloneRepoToTemp(cloneUrl: string): string {
  const tmpCloneDir = path.join(
    os.tmpdir(),
    `opencli-clone-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,
  );

  try {
    execFileSync('git', ['clone', '--depth', '1', cloneUrl, tmpCloneDir], {
      encoding: 'utf-8',
      stdio: ['pipe', 'pipe', 'pipe'],
    });
  } catch (err) {
    throw new PluginError(`Failed to clone plugin: ${getErrorMessage(err)}`, 'Check the repository URL and your network connection.');
  }

  return tmpCloneDir;
}

function withTempClone<T>(cloneUrl: string, work: (cloneDir: string) => T): T {
  const tmpCloneDir = cloneRepoToTemp(cloneUrl);
  try {
    return work(tmpCloneDir);
  } finally {
    try { fs.rmSync(tmpCloneDir, { recursive: true, force: true }); } catch {}
  }
}

function resolveRemotePluginSource(lockEntry: LockEntry | undefined, dir: string): string {
  const source = resolvePluginSource(lockEntry, dir);
  if (!source || source.kind === 'local') {
    throw new Error(`Unable to determine remote source for plugin at ${dir}`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the repository URL exists and is reachable: run `git ls-remote <cloneUrl>` manually.
  2. Check network/proxy/VPN; configure git proxy if needed (`git config --global http.proxy`).
  3. For private repos, set up credentials (SSH key, HTTPS token via credential helper) that match the URL scheme.
  4. Confirm git is installed and on PATH (`git --version`).
  5. Retry if the failure was transient (flaky network).

Example fix

// before
await installPlugin('github:user/typo-repo');
// after
await installPlugin('github:user/correct-repo'); // verified via git ls-remote
Defensive patterns

Strategy: try-catch

Validate before calling

import { execFileSync } from 'node:child_process';
function canClone(url) {
  try { execFileSync('git', ['ls-remote', url], { stdio: 'pipe' }); return true; }
  catch { return false; }
}

Try / catch

try {
  installPlugin('github:user/repo');
} catch (err) {
  if (err instanceof PluginError && err.message.startsWith('Failed to clone plugin')) {
    // check URL/network, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: installPlugin(source) or updatePlugin(name) with a git/monorepo source calls withTempClone -> cloneRepoToTemp; thrown when git clone fails: nonexistent repo, 404 on private repo without credentials, no network/DNS, proxy blocks github.com, or git is not installed.

Common situations: Typo in the GitHub user/repo; private repo with no SSH key or token; corporate firewall/proxy; offline laptop; git not on PATH (rare on dev machines); repo renamed or deleted.

Related errors


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