{"record":{"id":"da3f88508aebb2fd","repo":"abhigyanpatwari/GitNexus","slug":"multiple-repositories-indexed-specify-which-one-w","errorCode":null,"errorMessage":"Multiple repositories indexed. Specify which one with the \"repo\" parameter. Available: ${labels.join(', ')}","messagePattern":"Multiple repositories indexed\\. Specify which one with the \"repo\" parameter\\. Available: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/mcp/local/local-backend.ts","lineNumber":1744,"sourceCode":"    }\n\n    // Build a disambiguated \"Available: …\" list (#829). When two handles\n    // share a name, annotate each colliding label with its path so the\n    // caller can actually pick the right one. Single-name entries render\n    // identically to pre-#829 output.\n    const nameCounts = new Map<string, number>();\n    for (const h of this.repos.values()) {\n      const key = h.name.toLowerCase();\n      nameCounts.set(key, (nameCounts.get(key) ?? 0) + 1);\n    }\n    const labels = [...this.repos.values()].map((h) =>\n      (nameCounts.get(h.name.toLowerCase()) ?? 0) > 1 ? `${h.name} (${h.repoPath})` : h.name,\n    );\n\n    if (repoParam) {\n      throw new Error(`Repository \"${repoParam}\" not found. Available: ${labels.join(', ')}`);\n    }\n    throw new Error(\n      `Multiple repositories indexed. Specify which one with the \"repo\" parameter. Available: ${labels.join(', ')}`,\n    );\n  }\n\n  /**\n   * Re-point a resolved repo handle at a specific branch index (#2106).\n   *\n   * - No `branch` (default) → the flat workspace handle, unchanged (backward\n   *   compatible: every existing caller passes no branch).\n   * - `branch` equal to the flat slot's **on-disk** recorded branch → the\n   *   flat handle. The disk meta is read before any cached state is trusted\n   *   (#2364 review F1): the flat slot follows the checked-out working tree\n   *   (#2354), so a plain analyze after a branch switch restamps the meta\n   *   without any repo-resolution miss that would refresh a long-lived\n   *   server's cached handle — the cached label can otherwise serve another\n   *   branch's content under the old name (the pool staleness reinit\n   *   hot-swaps content without updating `handle.branch`).\n   * - `branch` matching an indexed pinned branch → a handle whose","sourceCodeStart":1726,"sourceCodeEnd":1762,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/mcp/local/local-backend.ts#L1726-L1762","documentation":"Repo resolution reached its fallback with no repo parameter supplied while the registry holds multiple repos: there is no single default to choose, so LocalBackend throws and lists all handles (paths added to duplicate names per #829). The caller must disambiguate via the repo parameter — or rely on cwd-based auto-selection, which already had its chance via pickRepoHandleForCwd.","triggerScenarios":"Indexing two or more repos (e.g. a CLI package and its web app) and then calling impact/query/context without a repo field from a working directory that is not inside any indexed repo.","commonSituations":"MCP client configs written when only one repo was indexed, then a second repo gets added; agents running from a neutral directory (home, /tmp) while multiple projects are indexed; shared team MCP servers serving several repos.","solutions":["Add the repo parameter with one of the listed names (or an absolute path for duplicate names).","Or run the client from inside the target repository's directory so cwd-based selection can auto-pick it.","Call list_repos first to enumerate valid handles and their exact labels.","If you only ever work with one repo, consider removing the others from the registry to restore implicit defaulting."],"exampleFix":"# before: ambiguous call, two repos indexed\n{\"tool\": \"context\", \"args\": {\"name\": \"parseRepo\"}}\n# → Multiple repositories indexed. Specify which one with the \"repo\" parameter. ...\n\n# after: explicit repo (name, or absolute path when names collide)\n{\"tool\": \"context\", \"args\": {\"name\": \"parseRepo\", \"repo\": \"gitnexus\"}}","handlingStrategy":"validation","validationCode":"// Make the repo param mandatory in your own client wrapper\nasync function withRepo<T>(tool: string, args: Record<string, unknown>, cwd: string): Promise<T> {\n  if (args.repo) return client.callTool({ name: tool, arguments: args }) as Promise<T>;\n  const { repositories } = await client.callTool({ name: 'list_repos', arguments: {} });\n  if (repositories.length === 1) return client.callTool({ name: tool, arguments: { ...args, repo: repositories[0].name } }) as Promise<T>;\n  const inferred = repositories.find((r: any) => cwd.startsWith(r.repo_path ?? r.path));\n  if (!inferred) throw new Error(`Multiple repos indexed (${repositories.length}) — pass repo or run from inside one`);\n  return client.callTool({ name: tool, arguments: { ...args, repo: inferred.name } }) as Promise<T>;\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await client.callTool({ name: 'impact', arguments: args });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Multiple repositories indexed')) {\n    const available = err.message.split('Available: ')[1];\n    throw new UserChoiceError(`Pick a repo: ${available}`); // surface the choice, never guess\n  }\n  throw err;\n}","preventionTips":["Always populate the repo field in multi-repo setups; never rely on implicit defaulting.","Run the MCP client from inside the target checkout so cwd-based selection can work.","Cache the list_repos result and prompt/select when multiple entries exist.","Prune stale entries from the registry when a project is archived."],"tags":["mcp","repo-resolution","ambiguity","multi-repo","registry"],"backgroundTag":"ambiguous-repository-selection","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}