{"record":{"id":"d388534f7ce1e79b","repo":"abhigyanpatwari/GitNexus","slug":"links-i-type-link-type-is-invalid-expect","errorCode":null,"errorMessage":"links[${i}].type \"${link.type}\" is invalid. Expected: ${VALID_CONTRACT_TYPES.join(', ')}","messagePattern":"links\\[(.+?)\\]\\.type \"(.+?)\" is invalid\\. Expected: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/group/config-parser.ts","lineNumber":85,"sourceCode":"      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,\n      to: link.to as string,\n      type: link.type as ManifestContractType,\n      contract: String(link.contract),","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/52924ef12c2290ceee4612526a828ec4cdf2047f/gitnexus/src/core/group/config-parser.ts#L67-L103","documentation":"Thrown while validating a group.yaml links entry: the link's `type` is not in the supported contract-type enum — http, grpc, thrift, topic, lib, custom, include. The message lists the full accepted set. Type decides which contract extractor handles the link during group sync, so unknown values cannot be defaulted.","triggerScenarios":"A links[i].type that is misspelled (rest, kafka, library), uses a synonym outside the enum (message-queue for topic), is missing (undefined fails includes), or has different casing (Http — YAML under JSON_SCHEMA keeps case, and the enum is lowercase).","commonSituations":"Describing a REST dependency as `rest` instead of `http`; labeling a Kafka link `kafka` instead of `topic`; C/C++ header dependency written as `header` instead of the opt-in `include` type.","solutions":["Replace the value with one of the enumerated types exactly as printed in the message: http, grpc, thrift, topic, lib, custom, include.","For REST/HTTP APIs use http; for message brokers use topic; for shared code dependencies use lib; for C/C++ header tracking use include (requires detect.includes: true).","If none fit, use custom rather than inventing a new token."],"exampleFix":"# before (group.yaml)\nlinks:\n  - from: services/web\n    to: services/api\n    type: rest\n# -> Error: links[0].type \"rest\" is invalid. Expected: http, grpc, thrift, topic, lib, custom, include\n\n# after\nlinks:\n  - from: services/web\n    to: services/api\n    type: http","handlingStrategy":"validation","validationCode":"import yaml from 'js-yaml';\n\nconst VALID_CONTRACT_TYPES = ['http', 'grpc', 'thrift', 'topic', 'lib', 'custom', 'include'];\n\nexport function validateLinkTypes(content: string): void {\n  const doc = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as Record<string, any>;\n  (doc.links ?? []).forEach((link: any, i: number) => {\n    if (!VALID_CONTRACT_TYPES.includes(link?.type)) {\n      throw new Error(\n        `links[${i}].type must be one of ${VALID_CONTRACT_TYPES.join(', ')} — got ${JSON.stringify(link?.type)}`,\n      );\n    }\n  });\n}","typeGuard":"const VALID_CONTRACT_TYPES = new Set(['http', 'grpc', 'thrift', 'topic', 'lib', 'custom', 'include']);\n\nexport const isContractType = (t: unknown): t is 'http' | 'grpc' | 'thrift' | 'topic' | 'lib' | 'custom' | 'include' =>\n  typeof t === 'string' && VALID_CONTRACT_TYPES.has(t);","tryCatchPattern":"try {\n  config = await loadGroupConfig(groupDir);\n} catch (err) {\n  if (err instanceof Error && /links\\[\\d+\\]\\.type .* is invalid/.test(err.message)) {\n    throw new Error(\n      'link type must be one of: http, grpc, thrift, topic, lib, custom, include (REST=http, broker=topic)',\n      { cause: err },\n    );\n  }\n  throw err;\n}","preventionTips":["Keep the enum in mind when authoring: REST/HTTP APIs -> http; message brokers -> topic; shared libraries -> lib; C/C++ headers -> include.","Use a YAML schema/linter with the seven allowed values for links[].type.","When no builtin fits, use custom — never invent a new token."],"tags":["yaml","configuration","validation","group","links","enum"],"backgroundTag":"invalid-config-value","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"}