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
- Re-run with the absolute repo path — path matches are unique and always win
- List registered repos first (gitnexus list / status) and copy the exact path
- 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
- Prefer absolute repo paths as command targets whenever a name might be duplicated
- Register duplicates under distinct --name aliases to keep -r unambiguous
- Parse the candidate listing from the error to offer the user an explicit choice
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
- Repository "${repoParam}" not found. Available: ${labels.joi
- Multiple repositories indexed. Specify which one with the "r
- Multiple registered repos match "${target}": ${listing} Pass
- Registry name "${registryName}" is already used by "${existi
- No registered repo matches "${target}".${hint}
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/358ab7b84731cc16.
Report an issue: GitHub.