{"record":{"id":"a97caabff799cfed","repo":"abhigyanpatwari/GitNexus","slug":"repository-repoparam-not-found-available","errorCode":null,"errorMessage":"Repository \"${repoParam}\" not found. Available: ${labels.join(', ')}","messagePattern":"Repository \"(.+?)\" not found\\. Available: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/mcp/local/local-backend.ts","lineNumber":1742,"sourceCode":"    if (this.repos.size === 0) {\n      throw new Error('No indexed repositories. Run: gitnexus analyze');\n    }\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","sourceCodeStart":1724,"sourceCodeEnd":1760,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/mcp/local/local-backend.ts#L1724-L1760","documentation":"Repo resolution failed for a caller-supplied repoParam even after refreshing the registry, and at least one other repo IS indexed — so the error enumerates the available handles as a disambiguated list (#829): when two handles share a name, each label is annotated with its repoPath so the caller can pick correctly.","triggerScenarios":"Passing repo: \"myrepo\" when the indexed name differs (rename, typo, wrong casing beyond the case-insensitive match), passing an id that no longer exists, or targeting a repo indexed under a different absolute path after it was moved/cloned elsewhere.","commonSituations":"Hard-coded repo names in MCP client configs drifting after project renames; moving or re-cloning a repo to a new directory so the registered handle no longer matches; multiple machines with different indexed sets; stale docs referencing old repo names.","solutions":["Read the 'Available: ...' list in the error (or call list_repos) and pass one of those exact names.","For duplicate names, copy the label's parenthesized absolute path and pass that path as repo.","If the target repo genuinely is not indexed, run `gitnexus analyze` inside it, then retry.","If the repo moved on disk, re-run analyze from its new location so the registry records the current path."],"exampleFix":"# before: stale name after rename\n{\"tool\": \"impact\", \"args\": {\"target\": \"oldName:login\", \"repo\": \"old-project\"}}\n\n# after: use a name from the Available list (or its absolute path)\n{\"tool\": \"impact\", \"args\": {\"target\": \"login\", \"repo\": \"renamed-project\"}}\n# duplicate names → disambiguate by path:\n{\"tool\": \"impact\", \"args\": {\"target\": \"login\", \"repo\": \"/srv/checkouts/a/renamed-project\"}}","handlingStrategy":"validation","validationCode":"// Validate the repo param against the live registry before tool calls\nconst { repositories } = await client.callTool({ name: 'list_repos', arguments: {} });\nconst valid = new Set(repositories.flatMap((r: any) => [r.name, r.repo_path ?? r.path]));\n\nfunction assertKnownRepo(repoParam: string): void {\n  if (!valid.has(repoParam) && !valid.has(repoParam.toLowerCase())) {\n    throw new Error(`repo \"${repoParam}\" not in registry. Known: ${[...valid].join(', ')}`);\n  }\n}","typeGuard":"const isKnownRepoHandle = (name: string, repos: Array<{ name: string; repoPath?: string }>): boolean =>\n  repos.some((r) => r.name.toLowerCase() === name.toLowerCase() || r.repoPath === name);","tryCatchPattern":"try {\n  return await client.callTool({ name: 'context', arguments: { ...args, repo } });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Repository \"')) {\n    // parse the Available list and re-ask the user / re-resolve the name\n    const available = err.message.split('Available: ')[1]?.split(', ') ?? [];\n    throw new UserChoiceError(`Unknown repo \"${repo}\". Pick one of: ${available.join(', ')}`);\n  }\n  throw err;\n}","preventionTips":["Fetch list_repos once at client startup and validate every repo param against it.","Re-run analyze after renaming or moving a repository so the registry stays current.","Prefer absolute paths as stable repo identifiers across machines.","For duplicate names, always use the parenthesized path from the error's Available list."],"tags":["mcp","repo-resolution","not-found","registry","disambiguation"],"backgroundTag":"repository-not-found","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}