abhigyanpatwari/GitNexus · error

Refusing to update ${safeTarget}: local changes detected. Se

Error message

Refusing to update ${safeTarget}: local changes detected. Set overwrite_local_changes: true to overwrite them.

What it means

During a pull/update, cloneOrPull runs 'git status --porcelain' in the existing target; if the output is non-empty the working tree has local modifications, and the update is refused rather than silently clobbered. The error tells you to opt in explicitly with overwrite_local_changes: true. It protects uncommitted user work in the clone.

Source

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

    if (options?.allowedCloneRoot) {
      await assertNoSymlinkPath(cloneRoot, path.join(safeTarget, '.git'), true);
    }
    await assertPostRealpathContainment(cloneRoot, safeTarget);
    // Confirm the existing clone is actually the same repository the caller
    // requested. Without this check, a pull would silently succeed against
    // whatever remote the dir was originally cloned from.
    await assertRemoteMatchesRequestedUrl(safeTarget, url, options?.timeoutMs);
    onProgress?.({ phase: 'pulling', message: 'Pulling latest changes...' });
    const runGitImpl = options?.runGitForTest ?? runGit;
    if (options?.branch) {
      if (!options.overwriteLocalChanges) {
        const status = await runGitImpl(['status', '--porcelain'], safeTarget, {
          token: options?.token,
          url,
          timeoutMs: options?.timeoutMs,
        });
        if (status.trim()) {
          throw new Error(
            `Refusing to update ${safeTarget}: local changes detected. Set overwrite_local_changes: true to overwrite them.`,
          );
        }
      }
      await runGitImpl(
        [
          'fetch',
          '--depth',
          '1',
          'origin',
          `refs/heads/${options.branch}:refs/remotes/origin/${options.branch}`,
        ],
        safeTarget,
        {
          token: options?.token,
          url,
          timeoutMs: options?.timeoutMs,
        },

View on GitHub (pinned to 0d1aed942f)

Solutions

  1. Commit or discard the local changes in the target repo, then retry the sync.
  2. Pass overwrite_local_changes: true in the cloneOrPull options if the changes are disposable.
  3. Move/stash the changes (git stash) or copy them aside before re-running the sync.
  4. Clean untracked files (git clean -fd) if they are the trigger and are not needed.

Example fix

// before
await cloneOrPull({ url, targetDir });
// after (changes are intentionally disposable)
await cloneOrPull({ url, targetDir, overwrite_local_changes: true });
Defensive patterns

Strategy: try-catch

Validate before calling

const { stdout } = await exec('git status --porcelain', { cwd: targetDir });
if (stdout.trim()) {
  console.warn('target has local changes; commit, stash, or set overwrite_local_changes: true');
}
await cloneOrPull({ url, targetDir, overwrite_local_changes: stdout.trim() ? true : false });

Try / catch

try {
  await cloneOrPull(opts);
} catch (err) {
  if ((err as Error).message.includes('local changes detected')) {
    // surface to operator instead of silently overwriting
    throw new Error(`Sync blocked: ${opts.targetDir} has uncommitted work. Resolve or set overwrite_local_changes.`, { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling cloneOrPull for a repo whose targetDir already exists as a git repo with uncommitted modifications or untracked files, without overwrite_local_changes: true.

Common situations: A developer edited files inside the auto-synced clone; build artifacts or generated files left untracked in the target; a crashed previous run left partial edits; hotfixes applied directly in the synced checkout.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


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