abhigyanpatwari/GitNexus · error · Error

Clone target already exists but is not a git repository: ${s

Error message

Clone target already exists but is not a git repository: ${safeTarget}

What it means

When the target is not (yet) recognized as a git repository and targetDir already exists non-empty, cloneOrPull refuses to clone over it. Cloning into a non-empty, non-git directory would either fail or mix foreign files into the checkout, so the library fails fast instead.

Source

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

        // ignored paths must survive, and `-e /.gitnexus` is belt-and-braces
        // because `.git/info/exclude` is skipped on a read-only storage mount
        // and a freshly cloned repo may not have been analyzed yet at all.
        await runGitImpl(['clean', '--force', '-d', '-e', '/.gitnexus'], safeTarget, {
          token: options?.token,
          url,
          timeoutMs: options?.timeoutMs,
        });
      }
    } else {
      await runGitImpl(['pull', '--ff-only'], safeTarget, {
        token: options?.token,
        url,
        timeoutMs: options?.timeoutMs,
      });
    }
  } else {
    if (targetExists && (await fs.readdir(safeTarget)).length > 0) {
      throw new Error(`Clone target already exists but is not a git repository: ${safeTarget}`);
    }
    onProgress?.({ phase: 'cloning', message: `Cloning ${url}...` });
    try {
      const runGitImpl = options?.runGitForTest ?? runGit;
      const cloneArgs = options?.branch
        ? buildBranchCloneArgs(url, safeTarget, options.branch)
        : buildCloneArgs(url, safeTarget);
      await runGitImpl(cloneArgs, undefined, {
        token: options?.token,
        url,
        timeoutMs: options?.timeoutMs,
      });
      await assertPostRealpathContainment(cloneRoot, safeTarget);
    } catch (err: unknown) {
      if (options?.quarantineRoot) {
        const partialExists = await fs.access(safeTarget).then(
          () => true,
          () => false,

View on GitHub (pinned to 0d1aed942f)

Solutions

  1. Delete or empty the target directory, then retry the clone.
  2. Re-initialize it as a proper git repo (git init + set remote) if the content should be preserved, or clone elsewhere and migrate.
  3. Point targetDir at a fresh path inside the clone root.
  4. Investigate what left the non-git files there before removing them.

Example fix

// before
await cloneOrPull({ url, targetDir: '/clones/repo' }); // dir has stale files, no .git
// after
await fs.rm('/clones/repo', { recursive: true, force: true });
await cloneOrPull({ url, targetDir: '/clones/repo' });
Defensive patterns

Strategy: validation

Validate before calling

import { fs } from '...'; // node:fs/promises
let isRepo = false, empty = true;
try {
  await fs.access(path.join(targetDir, '.git'));
  isRepo = true;
} catch {}
try {
  empty = (await fs.readdir(targetDir)).length === 0;
} catch {}
if (!isRepo && !empty) throw new Error(`Clean or remove non-git directory ${targetDir} before cloning`);

Try / catch

try {
  await cloneOrPull(opts);
} catch (err) {
  if ((err as Error).message.startsWith('Clone target already exists but is not a git repository')) {
    await fs.rm(opts.targetDir, { recursive: true, force: true });
    return cloneOrPull(opts);
  }
  throw err;
}

Prevention

When it happens

Trigger: targetDir exists and contains files but has no .git directory (deleted .git, half-deleted clone, or a directory that was never a repo); a previous failed run left partial files after quarantine was skipped; the user pointed targetDir at an unrelated folder.

Common situations: Manual 'rm -rf .git' or antivirus deleting .git; a previous clone failing mid-write; reusing an existing project folder as the clone destination; path collisions from a previous differently-named clone layout.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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