jackwener/OpenCLI · error

Unable to determine remote source for plugin at ${dir}

Error message

Unable to determine remote source for plugin at ${dir}

What it means

resolveRemotePluginSource determines the remote git URL for an installed plugin from its lock entry or the plugin dir's git remote origin config. It throws a plain Error when the resolved source is missing or is of kind 'local', because a remote update (clone) cannot be performed for a locally-symlinked plugin. This guards updatePlugin's non-local branch.

Source

Thrown at src/plugin.ts:257

    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}`);
  }
  return source.url;
}

function pathExistsSync(p: string): boolean {
  try {
    fs.lstatSync(p);
    return true;
  } catch {
    return false;
  }
}

function resolveRepoContainedPath(repoRoot: string, subPath: string): string {
  const resolved = path.resolve(repoRoot, subPath);
  if (!resolved.startsWith(repoRoot + path.sep) && resolved !== repoRoot) {
    throw new PluginError(`Plugin path "${subPath}" escapes repo root.`);
  }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check ~/.opencli/plugins.lock.json: ensure the plugin entry has a valid source (git or monorepo) with a url.
  2. If the plugin is local-source, do not run remote update — reinstall/update via the local path.
  3. Fix the git remote in the install dir (`git remote set-url origin <url>`) so getPluginSource can resolve it.
  4. As a last resort, uninstall and reinstall the plugin from its remote source to regenerate the lock entry.

Example fix

// before (lock entry missing source)
{ "myplugin": { "commitHash": "abc", "installedAt": "..." } }
// after
{ "myplugin": { "source": { "kind": "git", "url": "https://github.com/user/repo.git" }, "commitHash": "abc", "installedAt": "..." } }
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from 'node:fs';
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf-8'));
const entry = lock[pluginName];
if (!entry?.source || entry.source.kind === 'local') {
  // cannot remote-update; reinstall or update locally instead
}

Type guard

function hasRemoteSource(entry) {
  return !!entry && typeof entry === 'object'
    && entry.source != null
    && (entry.source.kind === 'git' || entry.source.kind === 'monorepo')
    && typeof entry.source.url === 'string';
}

Try / catch

try {
  updatePlugin(name);
} catch (err) {
  if (err.message.startsWith('Unable to determine remote source')) {
    // fall back to reinstalling from a known remote source
  } else throw err;
}

Prevention

When it happens

Trigger: updatePlugin(name) on a plugin whose lock entry in ~/.opencli/plugins.lock.json is absent/corrupt and whose directory has no git remote (e.g. installed from a local path via 'local:' or file:// source but reached this branch), or a normalized-but-local source record.

Common situations: Manually edited or hand-pruned lock file; plugin installed from a local directory symlink but the local branch was bypassed; lock file written by an older opencli version lacking a valid source field.

Related errors


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