{"record":{"id":"c8fbb377b475c53d","repo":"vercel-labs/agent-skills","slug":"ambiguous-project-link","errorCode":"AMBIGUOUS_PROJECT_LINK","errorMessage":"AMBIGUOUS_PROJECT_LINK: `.vercel/repo.json` contains multiple projects. Run from the linked app directory, or pass the intended projectId together with VERCEL_ORG_ID.","messagePattern":"AMBIGUOUS_PROJECT_LINK: `\\.vercel/repo\\.json` contains multiple projects\\. Run from the linked app directory, or pass the intended projectId together with VERCEL_ORG_ID\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skills/vercel-optimize/lib/vercel.mjs","lineNumber":135,"sourceCode":"    await runVercel(['whoami']);\n  } catch {\n    throw new Error('NOT_AUTH: run `vercel login`.');\n  }\n}\n\nexport async function getCliIdentity() {\n  const r = await runVercelJson(['whoami', '--format', 'json']);\n  return r.ok ? r.data : null;\n}\n\n// Supports newer `.vercel/repo.json` (multi-project) + legacy `.vercel/project.json` (single-project).\nexport async function readProjectJson(cwd = process.cwd()) {\n  try {\n    const raw = await readFile(join(cwd, '.vercel', 'repo.json'), 'utf-8');\n    const parsed = JSON.parse(raw);\n    const projects = Array.isArray(parsed?.projects) ? parsed.projects.filter((p) => p?.id) : [];\n    if (projects.length > 1) {\n      throw new Error('AMBIGUOUS_PROJECT_LINK: `.vercel/repo.json` contains multiple projects. Run from the linked app directory, or pass the intended projectId together with VERCEL_ORG_ID.');\n    }\n    const first = projects[0];\n    if (first?.id) {\n      return { projectId: first.id, orgId: first.orgId ?? null, source: 'repo.json' };\n    }\n  } catch (err) {\n    if (err?.message?.startsWith('AMBIGUOUS_PROJECT_LINK:')) throw err;\n    /* fall through */\n  }\n\n  // Legacy single-project format.\n  try {\n    const raw = await readFile(join(cwd, '.vercel', 'project.json'), 'utf-8');\n    const parsed = JSON.parse(raw);\n    if (parsed?.projectId) {\n      return { projectId: parsed.projectId, orgId: parsed.orgId ?? null, source: 'project.json' };\n    }\n  } catch { /* fall through */ }","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/vercel-labs/agent-skills/blob/b8caa260a420a73042e35521de4b5c8baf6446cc/skills/vercel-optimize/lib/vercel.mjs#L117-L153","documentation":"Thrown by readProjectJson() when `.vercel/repo.json` (the newer multi-project link format) contains more than one project entry with an `id`. Because the collector cannot pick which project to query for metrics, it refuses to guess. The catch block rethrows this specific message so the ambiguity is surfaced rather than silently falling through to legacy project.json.","triggerScenarios":"A monorepo where `vercel link` linked multiple apps into one `.vercel/repo.json`; running collect-signals from the repo root instead of an individual linked app directory; a workspace that re-linked over an existing link file.","commonSituations":"Monorepo (Turborepo/Nx) with several Vercel projects; a developer who linked a second project into the same root; CI running at repo root for a polyrepo-style setup.","solutions":["`cd` into the specific linked app directory (the one whose metrics you want) and re-run collect-signals from there.","Pass the intended projectId as the first positional arg together with VERCEL_ORG_ID so resolveProjectId uses the explicit value instead of reading repo.json.","Re-link cleanly with `vercel link` from the app directory so repo.json holds a single project."],"exampleFix":"# before (run at monorepo root with multiple linked projects)\n$ node scripts/collect-signals.mjs\n-> AMBIGUOUS_PROJECT_LINK: `.vercel/repo.json` contains multiple projects.\n\n# after — scope to one app explicitly\n$ node scripts/collect-signals.mjs prj_abc123 --team my-team\n# or\n$ cd apps/web && node ../../scripts/collect-signals.mjs","handlingStrategy":"validation","validationCode":"import { readFile } from 'node:fs/promises';\nasync function assertSingleProject(cwd = process.cwd()) {\n  let parsed;\n  try { parsed = JSON.parse(await readFile(`${cwd}/.vercel/repo.json`, 'utf-8')); }\n  catch { return; } // no repo.json, fine\n  const ids = (parsed.projects ?? []).filter(p => p?.id);\n  if (ids.length > 1) {\n    throw new Error(`repo.json links ${ids.length} projects; cd into the app dir or pass projectId + VERCEL_ORG_ID`);\n  }\n}\nawait assertSingleProject();","typeGuard":null,"tryCatchPattern":"try {\n  await resolveProjectId();\n} catch (err) {\n  if (err.message.startsWith('AMBIGUOUS_PROJECT_LINK') && err.message.includes('multiple projects')) {\n    // ask user which app, then rerun with explicit projectId\n  }\n  throw err;\n}","preventionTips":["Run collect-signals from the linked app directory, not the monorepo root.","Keep one project per `.vercel` link directory.","Pass projectId + VERCEL_ORG_ID explicitly in CI to bypass link-file ambiguity."],"tags":["project-link","monorepo","config","vercel"],"backgroundTag":null,"analyzedSha":"b8caa260a420a73042e35521de4b5c8baf6446cc","analyzedAt":"2026-08-13T06:03:29.508Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}