{"record":{"id":"35cdf36a51b5fac6","repo":"abhigyanpatwari/GitNexus","slug":"links-i-from-link-from-does-not-match-any","errorCode":null,"errorMessage":"links[${i}].from \"${link.from}\" does not match any repo path in group","messagePattern":"links\\[(.+?)\\]\\.from \"(.+?)\" does not match any repo path in group","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/group/config-parser.ts","lineNumber":69,"sourceCode":"  }\n\n  if (raw.version === undefined) throw new Error('version is required in group.yaml');\n  if (raw.version !== 1) {\n    throw new Error(`Unsupported group.yaml version: ${raw.version}. Expected 1.`);\n  }\n  if (!raw.name || typeof raw.name !== 'string') throw new Error('name is required in group.yaml');\n  if (!raw.repos || typeof raw.repos !== 'object' || Array.isArray(raw.repos)) {\n    throw new Error('repos is required in group.yaml (must be a mapping)');\n  }\n\n  const repos = raw.repos as Record<string, string>;\n  const repoPaths = new Set(Object.keys(repos));\n\n  const rawLinks = (raw.links as unknown[]) || [];\n  const links: GroupManifestLink[] = rawLinks.map((l: unknown, i: number) => {\n    const link = l as Record<string, unknown>;\n    if (!link.from || !repoPaths.has(link.from as string)) {\n      throw new Error(`links[${i}].from \"${link.from}\" does not match any repo path in group`);\n    }\n    if (!link.to || !repoPaths.has(link.to as string)) {\n      throw new Error(`links[${i}].to \"${link.to}\" does not match any repo path in group`);\n    }\n    if (!VALID_CONTRACT_TYPES.includes(link.type as ContractType)) {\n      throw new Error(\n        `links[${i}].type \"${link.type}\" is invalid. Expected: ${VALID_CONTRACT_TYPES.join(', ')}`,\n      );\n    }\n    if (!VALID_ROLES.includes(link.role as ContractRole)) {\n      throw new Error(`links[${i}].role \"${link.role}\" is invalid. Expected: provider | consumer`);\n    }\n    if (\n      link.contract === undefined ||\n      link.contract === null ||\n      String(link.contract).trim() === ''\n    ) {\n      throw new Error(`links[${i}].contract is required`);","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/group/config-parser.ts#L51-L87","documentation":"Thrown during links[] validation when a link's 'from' field is missing or does not match any key in the parsed 'repos' mapping. Every link must connect two repos that are declared in repos, so the parser builds a repoPaths set from repos' keys and checks each link endpoint against it. The index i and the offending value are included.","triggerScenarios":"A link with from: './services/api' when the repos mapping key is 'services/api' (path-prefix mismatch); a link referencing a repo that was never added to repos; a link missing the 'from' field entirely.","commonSituations":"Inconsistent path conventions (leading './' in one place but not the other); a repo renamed in repos but not in links; a link copy-pasted from another group without updating its endpoints.","solutions":["Compare the link.from value verbatim against the keys in the repos mapping — they must match exactly (including leading './' or trailing slash).","Add the missing repo to repos, or correct the path in the link to point at an existing repo key.","Use the index i from the message to locate the exact offending link entry."],"exampleFix":"# before — from uses ./services/api but repos key is services/api\nrepos:\n  services/api: ./services/api\nlinks:\n  - { from: ./services/api, to: services/worker, type: http, contract: GET /v1, role: provider }\n\n# after — keys match exactly\nrepos:\n  services/api: ./services/api\nlinks:\n  - { from: services/api, to: services/worker, type: http, contract: GET /v1, role: provider }","handlingStrategy":"validation","validationCode":"function validateLinksAgainstRepos(raw: Record<string, unknown>): string[] {\n  const repos = raw.repos as Record<string, string>;\n  const repoPaths = new Set(Object.keys(repos));\n  const links = (raw.links as unknown[]) || [];\n  const errs: string[] = [];\n  links.forEach((l, i) => {\n    const link = l as Record<string, unknown>;\n    if (typeof link.from !== 'string' || !repoPaths.has(link.from)) {\n      errs.push(`links[${i}].from \"${String(link.from)}\" not in repos`);\n    }\n  });\n  return errs;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep a single canonical path convention (with or without leading './') in both repos keys and link endpoints.","When renaming a repo, update both the repos mapping and every link.from/link.to referencing it.","Validate the manifest with a pre-flight script that cross-checks link endpoints against repos keys."],"tags":["yaml","group-config","links","validation","config-parser"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}