{"record":{"id":"d0866f28417e7241","repo":"abhigyanpatwari/GitNexus","slug":"existing-clone-at-targetdir-has-no-remote-origi","errorCode":null,"errorMessage":"Existing clone at ${targetDir} has no remote.origin — refusing to pull","messagePattern":"Existing clone at (.+?) has no remote\\.origin — refusing to pull","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/server/git-clone.ts","lineNumber":285,"sourceCode":" *\n * Closes the wrong-repo silent-analysis vector that Codex's adversarial\n * review on PR #1325 surfaced: clone dirs are keyed by URL basename, so a\n * request for `https://gitlab.example/attacker/repo.git` would otherwise\n * collide with an existing `~/.gitnexus/repos/repo` cloned from a different\n * origin and `git pull --ff-only` would silently succeed against the wrong\n * remote.\n *\n * Exported so the comparison logic is testable in isolation against any\n * tmpdir-based fixture, without needing to populate CLONE_ROOT.\n */\nexport async function assertRemoteMatchesRequestedUrl(\n  targetDir: string,\n  requestedUrl: string,\n  timeoutMs?: number,\n): Promise<void> {\n  const remoteUrl = await getRemoteOriginUrl(targetDir, timeoutMs);\n  if (remoteUrl === null) {\n    throw new Error(`Existing clone at ${targetDir} has no remote.origin — refusing to pull`);\n  }\n  if (normalizeGitUrlForCompare(remoteUrl) !== normalizeGitUrlForCompare(requestedUrl)) {\n    throw new Error(\n      // Both URLs are echoed to the API caller and the server log, and either\n      // can carry `https://user:token@` userinfo — strip it here too (#2914).\n      `Existing clone at ${targetDir} has remote ${stripUrlCredentials(remoteUrl)}, ` +\n        `not the requested URL ${stripUrlCredentials(requestedUrl)}`,\n    );\n  }\n}\n\n/**\n * Clone or pull a git repository.\n * If targetDir doesn't exist: git clone --depth 1\n * If targetDir exists with .git: git pull --ff-only (after verifying the\n * existing clone's remote.origin matches the requested URL).\n *\n * Security:","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/server/git-clone.ts#L267-L303","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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","Or repair it in place: cd into the dir and `git remote add origin <url>` so remote.origin exists again","Verify git is installed and on PATH for the server process — a failing `git config` spawn also yields null","Guard future runs by always letting gitnexus derive the target via getCloneDir(extractRepoName(url)) instead of reusing arbitrary directories"],"exampleFix":"# before: broken clone dir with no remote.origin\nls ~/.gitnexus/repos/myrepo/.git  # exists, but `git -C ~/.gitnexus/repos/myrepo config --get remote.origin.url` prints nothing\n# after: remove so cloneOrPull re-clones cleanly\nrm -rf ~/.gitnexus/repos/myrepo\n# (or repair: git -C ~/.gitnexus/repos/myrepo remote add origin https://github.com/org/myrepo.git)","handlingStrategy":"validation","validationCode":"import { getRemoteOriginUrl } from './git-clone.js';\nconst remote = await getRemoteOriginUrl(targetDir);\nif (remote === null) {\n  // stale/broken checkout: clean it up instead of letting cloneOrPull throw\n  await fs.rm(targetDir, { recursive: true, force: true });\n}","typeGuard":null,"tryCatchPattern":"try {\n  await cloneOrPull(url, dir);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('has no remote.origin')) {\n    await fs.rm(dir, { recursive: true, force: true });\n    await cloneOrPull(url, dir); // fresh clone now that the broken dir is gone\n  } else throw err;\n}","preventionTips":["Always derive clone dirs from getCloneDir(extractRepoName(url)) so directory state matches what cloneOrPull expects","Verify git is installed and on PATH for the server process — a failing `git config` spawn also yields a null remote","Treat CLONE_ROOT as gitnexus-owned storage: never hand-init or copy repos into it"],"tags":["git","git-clone","remote-origin","stale-state","integrity-guard"],"backgroundTag":"git-remote-origin-missing","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-09-08T00:40:44.970Z","contentChangedAt":"2026-09-08T00:40:44.970Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}