abhigyanpatwari/GitNexus · error

Existing clone at ${targetDir} has no remote.origin — refusi

Error message

Existing clone at ${targetDir} has no remote.origin — refusing to pull

What it means

cloneOrPull refuses to `git pull --ff-only` an existing checkout whose remote.origin.url cannot be read (getRemoteOriginUrl returned null — `git config --get remote.origin.url` produced nothing or the git process errored). The check exists because clone directories are keyed by URL basename; pulling without knowing the remote would update some unknown repository and silently analyze the wrong code (the wrong-repo vector from the adversarial review of PR #1325). This is an integrity failure of the on-disk clone state, not a network error.

Source

Thrown at gitnexus/src/server/git-clone.ts:285

 *
 * Closes the wrong-repo silent-analysis vector that Codex's adversarial
 * review on PR #1325 surfaced: clone dirs are keyed by URL basename, so a
 * request for `https://gitlab.example/attacker/repo.git` would otherwise
 * collide with an existing `~/.gitnexus/repos/repo` cloned from a different
 * origin and `git pull --ff-only` would silently succeed against the wrong
 * remote.
 *
 * Exported so the comparison logic is testable in isolation against any
 * tmpdir-based fixture, without needing to populate CLONE_ROOT.
 */
export async function assertRemoteMatchesRequestedUrl(
  targetDir: string,
  requestedUrl: string,
  timeoutMs?: number,
): Promise<void> {
  const remoteUrl = await getRemoteOriginUrl(targetDir, timeoutMs);
  if (remoteUrl === null) {
    throw new Error(`Existing clone at ${targetDir} has no remote.origin — refusing to pull`);
  }
  if (normalizeGitUrlForCompare(remoteUrl) !== normalizeGitUrlForCompare(requestedUrl)) {
    throw new Error(
      // Both URLs are echoed to the API caller and the server log, and either
      // can carry `https://user:token@` userinfo — strip it here too (#2914).
      `Existing clone at ${targetDir} has remote ${stripUrlCredentials(remoteUrl)}, ` +
        `not the requested URL ${stripUrlCredentials(requestedUrl)}`,
    );
  }
}

/**
 * Clone or pull a git repository.
 * If targetDir doesn't exist: git clone --depth 1
 * If targetDir exists with .git: git pull --ff-only (after verifying the
 * existing clone's remote.origin matches the requested URL).
 *
 * Security:

View on GitHub (pinned to 0d1aed942f)

Solutions

  1. Delete the broken clone directory (rm -rf ~/.gitnexus/repos/<name>, or the /data/gitnexus/repos path under GITNEXUS_HOME in Docker) so the next call does a fresh clone
  2. Or repair it in place: cd into the dir and `git remote add origin <url>` so remote.origin exists again
  3. Verify git is installed and on PATH for the server process — a failing `git config` spawn also yields null
  4. Guard future runs by always letting gitnexus derive the target via getCloneDir(extractRepoName(url)) instead of reusing arbitrary directories

Example fix

# before: broken clone dir with no remote.origin
ls ~/.gitnexus/repos/myrepo/.git  # exists, but `git -C ~/.gitnexus/repos/myrepo config --get remote.origin.url` prints nothing
# after: remove so cloneOrPull re-clones cleanly
rm -rf ~/.gitnexus/repos/myrepo
# (or repair: git -C ~/.gitnexus/repos/myrepo remote add origin https://github.com/org/myrepo.git)
Defensive patterns

Strategy: validation

Validate before calling

import { getRemoteOriginUrl } from './git-clone.js';
const remote = await getRemoteOriginUrl(targetDir);
if (remote === null) {
  // stale/broken checkout: clean it up instead of letting cloneOrPull throw
  await fs.rm(targetDir, { recursive: true, force: true });
}

Try / catch

try {
  await cloneOrPull(url, dir);
} catch (err) {
  if (err instanceof Error && err.message.includes('has no remote.origin')) {
    await fs.rm(dir, { recursive: true, force: true });
    await cloneOrPull(url, dir); // fresh clone now that the broken dir is gone
  } else throw err;
}

Prevention

When it happens

Trigger: cloneOrPull(url, dir) where dir contains a .git directory but has no remote.origin configured — e.g. the directory was created by `git init` + manual copy, `git remote remove origin` was run, .git/config was truncated/corrupted, or the clone was made with a detached .git dir. Also triggered if the `git config` subprocess fails to spawn (proc error path resolves null).

Common situations: Stale or hand-crafted entries under CLONE_ROOT (~/.gitnexus/repos, or /data/gitnexus/repos in Docker); a repo copied with rsync/scp in a way that lost .git/config; CI containers where git is missing or PATH is broken so every git query resolves null; a previous clone interrupted mid-write leaving a broken .git.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@0d1aed942f (2026-09-08). Data as JSON: /api/errors/d0866f28417e7241. Report an issue: GitHub.