{"record":{"id":"363dbb0105c815e3","repo":"nexu-io/open-design","slug":"multiple-projects-match-arg-opts-pass-th","errorCode":null,"errorMessage":"multiple projects match \"${arg}\": ${opts}. Pass the UUID instead.","messagePattern":"multiple projects match \"(.+?)\": (.+?)\\. Pass the UUID instead\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/daemon/src/mcp.ts","lineNumber":2996,"sourceCode":"      .replace(/[\\s_-]+/g, '-');\n  const target = norm(arg);\n\n  const idMatch = list.find((p) => p.id === arg);\n  if (idMatch) return { id: idMatch.id, name: idMatch.name, source: 'id' as const };\n\n  const exact = list.filter((p) => String(p.name || '').toLowerCase() === lower);\n  if (exact.length === 1) { const p = exact[0]!; return { id: p.id, name: p.name, source: 'exact' as const }; }\n\n  const slugged = list.filter((p) => norm(p.name) === target);\n  if (slugged.length === 1) { const p = slugged[0]!; return { id: p.id, name: p.name, source: 'slug' as const }; }\n\n  const subs = list.filter((p) =>\n    String(p.name || '').toLowerCase().includes(lower),\n  );\n  if (subs.length === 1) { const p = subs[0]!; return { id: p.id, name: p.name, source: 'substring' as const }; }\n  if (subs.length > 1) {\n    const opts = subs.map((p) => `${p.name} (${p.id})`).join(', ');\n    throw new Error(\n      `multiple projects match \"${arg}\": ${opts}. Pass the UUID instead.`,\n    );\n  }\n  throw new Error(`no project matches \"${arg}\"`);\n}\n\nasync function getJson<T>(url: string, headers?: Record<string, string>): Promise<T> {\n  const resp = await fetch(url, headers ? { headers } : undefined);\n  if (!resp.ok) {\n    const body = await safeText(resp);\n    throw new Error(`daemon ${resp.status} on ${url}: ${body || resp.statusText}`);\n  }\n  return (await resp.json()) as T;\n}\n\nasync function getFile(\n  baseUrl: string,\n  project: string,","sourceCodeStart":2978,"sourceCodeEnd":3014,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/mcp.ts#L2978-L3014","documentation":"Thrown by resolveProjectId when more than one project's name contains the supplied substring and no exact/slug/unique match won. The resolver tries exact name, normalized slug, then substring fallback; if substring yields >1 it refuses to guess. The message lists each candidate as `name (id)`.","triggerScenarios":"Passing a short or common fragment like `project: \"test\"` when `test-1`, `test-2`, and `latest-test` all exist; passing a partial UUID prefix that matches several.","commonSituations":"Multiple similarly-named projects accumulated over time; template-generated names sharing a prefix; passing a team or category substring instead of a specific project name.","solutions":["Pass the full UUID of the intended project as listed in the error message.","Use the exact full project name (case-insensitive) so the exact-match branch wins.","Rename projects to remove ambiguity, or archive duplicates.","List projects first to confirm names/ids before resolving."],"exampleFix":"// before\nrunStart({ project: \"test\" })  // matches test-1 and test-2\n// after\nrunStart({ project: \"9f3c1a2e-...-uuid\" })  // full UUID from the error","handlingStrategy":"validation","validationCode":"const matches = list.filter(p => p.name.toLowerCase().includes(q.toLowerCase()));\nif (matches.length > 1) throw new Error(`ambiguous; pass UUID: ${matches.map(m => m.id).join(', ')}`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always resolve with the full UUID when names may collide.","Use exact full names rather than substrings.","Rename or archive projects with confusingly similar names."],"tags":["mcp","project-resolution","ambiguity"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}