abhigyanpatwari/GitNexus · error · RegistryAmbiguousTargetError

Multiple registered repos match "${target}": ${listing} Pass

Error message

Multiple registered repos match "${target}":
${listing}
Pass the absolute path instead to disambiguate.

What it means

Target resolution for commands like gitnexus remove resolves tier by tier: canonical path match first (unique by registry construction), then case-insensitive name match. When more than one registry entry carries the requested name — possible after --allow-duplicate-name registrations — the command refuses to guess and throws with a listing of the candidates, telling you to pass the absolute path.

Source

Thrown at gitnexus/src/storage/repo-manager.ts:1334

  // Canonicalising the STORED entry (not just the input) is what gives
  // us backward-compat for registries written by versions that only
  // ran `path.resolve` — both get canonicalised here at compare time.
  const canonicalTarget = canonicalizePath(target);
  const pathMatch = entries.find((e) => {
    const a = canonicalizePath(e.path);
    const b = canonicalTarget;
    return registryPathEquals(a, b);
  });
  if (pathMatch) return pathMatch;

  // Tier 2: name match. Case-insensitive on all platforms — registry
  // name collisions are already filtered case-insensitively in
  // `registerRepo`, so "APP" vs "app" are considered the same key.
  const targetLower = target.toLowerCase();
  const nameMatches = entries.filter((e) => e.name.toLowerCase() === targetLower);
  if (nameMatches.length === 1) return nameMatches[0];
  if (nameMatches.length > 1) {
    throw new RegistryAmbiguousTargetError(target, nameMatches);
  }

  // Tier 3: miss. Build the available-names hint ONCE; resolveRepo-style
  // disambiguated labels (`app (/path)`) are applied when the same name
  // appears in multiple entries so the user sees the same hint shape as
  // `-r <name>` errors.
  const nameCounts = new Map<string, number>();
  for (const e of entries) {
    const key = e.name.toLowerCase();
    nameCounts.set(key, (nameCounts.get(key) ?? 0) + 1);
  }
  const availableNames = entries.map((e) =>
    (nameCounts.get(e.name.toLowerCase()) ?? 0) > 1 ? `${e.name} (${e.path})` : e.name,
  );
  throw new RegistryNotFoundError(target, availableNames);
};

/**

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Re-run with the absolute repo path — path matches are unique and always win
  2. List registered repos first (gitnexus list / status) and copy the exact path
  3. Prevent recurrence: register duplicates under distinct --name aliases so -r stays unambiguous

Example fix

# before
gitnexus remove app    # → ambiguous, two entries named 'app'

# after
gitnexus remove /home/me/forks/app
Defensive patterns

Strategy: validation

Validate before calling

const matches = entries.filter(
  (e) => e.name.toLowerCase() === target.toLowerCase(),
);
if (matches.length > 1) {
  target = matches[0].path; // or surface a picker to the user
}

Prevention

When it happens

Trigger: `gitnexus remove app` (or the MCP equivalent target argument) when two entries are registered under name 'app' at different paths.

Common situations: Prior --allow-duplicate-name use (upstream + fork, local + CI clone); shared machines where several users' clones got the same default name.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/358ab7b84731cc16. Report an issue: GitHub.