{"record":{"id":"0ad7c0151b227bb9","repo":"abhigyanpatwari/GitNexus","slug":"links-i-to-link-to-does-not-match-any-rep","errorCode":null,"errorMessage":"links[${i}].to \"${link.to}\" does not match any repo path in group","messagePattern":"links\\[(.+?)\\]\\.to \"(.+?)\" does not match any repo path in group","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/group/config-parser.ts","lineNumber":82,"sourceCode":"  const repos: Record<string, string> = {};\n  for (const [memberPath, registryName] of Object.entries(reposRaw)) {\n    if (typeof registryName !== 'string' || registryName.trim() === '') {\n      throw new Error(\n        `repos[\"${memberPath}\"] must be a non-empty registry name string, not ${typeof registryName}`,\n      );\n    }\n    repos[memberPath] = registryName.trim();\n  }\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 ManifestContractType)) {\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`);\n    }\n    return {\n      from: link.from as string,","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/52924ef12c2290ceee4612526a828ec4cdf2047f/gitnexus/src/core/group/config-parser.ts#L64-L100","documentation":"Thrown while validating a group.yaml links entry: the link's `to` value is missing or does not match any key of the repos mapping. Same rule as the from-side check — every link endpoint must be a declared repo path in this manifest — but for the destination side of the contract link.","triggerScenarios":"A links[i].to that references a repo path not present in repos: destination repo deleted from the group but the link kept, a typo, or an absolute path where the mapping uses relative keys.","commonSituations":"Removing a consumer repo from repos during a cleanup while its provider link stays; a link pasted from another group manifest whose paths differ; inconsistent use of nested vs flat path keys.","solutions":["Set links[i].to to the exact repos key of the destination repo (compare with the echoed value in the message).","Remove the stale link entirely if the destination repo is no longer part of the group.","After any repos key rename, sweep all links[].from and links[].to for the old key."],"exampleFix":"# before (group.yaml)\nrepos:\n  services/api: https://github.com/org/api\nlinks:\n  - from: services/api\n    to: service/web   # typo: key is services/web\n# -> Error: links[0].to \"service/web\" does not match any repo path in group\n\n# after\nlinks:\n  - from: services/api\n    to: services/web","handlingStrategy":"validation","validationCode":"import yaml from 'js-yaml';\n\n// Both link endpoints must be declared repos keys; catches `to` typos early.\nexport function validateLinkTargets(content: string): void {\n  const doc = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as Record<string, any>;\n  const repoPaths = new Set(Object.keys(doc.repos ?? {}));\n  const dangling = (doc.links ?? [])\n    .map((l: any, i: number) => ({ i, to: l?.to, ok: repoPaths.has(l?.to) }))\n    .filter((x: any) => !x.ok);\n  if (dangling.length) {\n    throw new Error(\n      `dangling link targets: ${dangling.map((d: any) => `links[${d.i}].to=${JSON.stringify(d.to)}`).join('; ')}`,\n    );\n  }\n}","typeGuard":"export const isKnownRepoPath = (p: unknown, repoPaths: Set<string>): p is string =>\n  typeof p === 'string' && repoPaths.has(p);","tryCatchPattern":"try {\n  config = await loadGroupConfig(groupDir);\n} catch (err) {\n  if (err instanceof Error && /links\\[\\d+\\]\\.to .* does not match any repo path/.test(err.message)) {\n    throw new Error(\n      'link destination is not a repo in this group — fix the repos key or delete the stale link',\n      { cause: err },\n    );\n  }\n  throw err;\n}","preventionTips":["When removing a repo from repos, delete or rewrite every link pointing at it in the same commit.","Validate the whole links list in one pass (see validationCode) instead of fixing one error per sync attempt.","Keep a naming convention for paths so to/from typos are visually obvious in review."],"tags":["yaml","configuration","validation","group","links","reference-error"],"backgroundTag":"unresolved-config-reference","analyzedSha":"52924ef12c2290ceee4612526a828ec4cdf2047f","analyzedAt":"2026-09-01T13:15:02.810Z","contentChangedAt":"2026-09-01T13:15:02.810Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}