vercel-labs/agent-skills · error · Error
AMBIGUOUS_PROJECT_LINK
AMBIGUOUS_PROJECT_LINK
Error message
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.
What it means
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.
Source
Thrown at skills/vercel-optimize/lib/vercel.mjs:135
await runVercel(['whoami']);
} catch {
throw new Error('NOT_AUTH: run `vercel login`.');
}
}
export async function getCliIdentity() {
const r = await runVercelJson(['whoami', '--format', 'json']);
return r.ok ? r.data : null;
}
// Supports newer `.vercel/repo.json` (multi-project) + legacy `.vercel/project.json` (single-project).
export async function readProjectJson(cwd = process.cwd()) {
try {
const raw = await readFile(join(cwd, '.vercel', 'repo.json'), 'utf-8');
const parsed = JSON.parse(raw);
const projects = Array.isArray(parsed?.projects) ? parsed.projects.filter((p) => p?.id) : [];
if (projects.length > 1) {
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.');
}
const first = projects[0];
if (first?.id) {
return { projectId: first.id, orgId: first.orgId ?? null, source: 'repo.json' };
}
} catch (err) {
if (err?.message?.startsWith('AMBIGUOUS_PROJECT_LINK:')) throw err;
/* fall through */
}
// Legacy single-project format.
try {
const raw = await readFile(join(cwd, '.vercel', 'project.json'), 'utf-8');
const parsed = JSON.parse(raw);
if (parsed?.projectId) {
return { projectId: parsed.projectId, orgId: parsed.orgId ?? null, source: 'project.json' };
}
} catch { /* fall through */ }View on GitHub (pinned to b8caa260a4)
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.
Example fix
# before (run at monorepo root with multiple linked projects) $ node scripts/collect-signals.mjs -> AMBIGUOUS_PROJECT_LINK: `.vercel/repo.json` contains multiple projects. # after — scope to one app explicitly $ node scripts/collect-signals.mjs prj_abc123 --team my-team # or $ cd apps/web && node ../../scripts/collect-signals.mjs
Defensive patterns
Strategy: validation
Validate before calling
import { readFile } from 'node:fs/promises';
async function assertSingleProject(cwd = process.cwd()) {
let parsed;
try { parsed = JSON.parse(await readFile(`${cwd}/.vercel/repo.json`, 'utf-8')); }
catch { return; } // no repo.json, fine
const ids = (parsed.projects ?? []).filter(p => p?.id);
if (ids.length > 1) {
throw new Error(`repo.json links ${ids.length} projects; cd into the app dir or pass projectId + VERCEL_ORG_ID`);
}
}
await assertSingleProject(); Try / catch
try {
await resolveProjectId();
} catch (err) {
if (err.message.startsWith('AMBIGUOUS_PROJECT_LINK') && err.message.includes('multiple projects')) {
// ask user which app, then rerun with explicit projectId
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- NO_PROJECT_ID
- PROJECT_SCOPE_UNRESOLVED
- PROJECT_SCOPE_MISMATCH
- VERCEL_NOT_INSTALLED
- VERCEL_VERSION_UNPARSEABLE
AI-assisted analysis of vercel-labs/agent-skills@b8caa260a4 (2026-08-13).
Data as JSON: /api/errors/c8fbb377b475c53d.
Report an issue: GitHub.