{"record":{"id":"f5b458fedc02b502","repo":"abhigyanpatwari/GitNexus","slug":"repos-is-required-in-group-yaml-must-be-a-mapping","errorCode":null,"errorMessage":"repos is required in group.yaml (must be a mapping)","messagePattern":"repos is required in group\\.yaml \\(must be a mapping\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/group/config-parser.ts","lineNumber":60,"sourceCode":"const DEFAULT_MATCHING = {\n  exclude_links_paths: [] as string[],\n  exclude_links_param_only_paths: false,\n};\n\nexport function parseGroupConfig(yamlContent: string): GroupConfig {\n  const raw = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA }) as Record<string, unknown>;\n\n  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n    throw new Error('Invalid YAML: expected an object');\n  }\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 reposRaw = raw.repos as Record<string, unknown>;\n  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)) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/52924ef12c2290ceee4612526a828ec4cdf2047f/gitnexus/src/core/group/config-parser.ts#L42-L78","documentation":"Thrown by parseGroupConfig() when the group.yaml object has no `repos` key, or when repos is not a mapping — an array or scalar both fail (Array.isArray check plus typeof 'object'). repos must map repo paths to their git URLs/locations; it defines both group membership and the valid path keys that links may reference.","triggerScenarios":"Loading a group.yaml where repos is a YAML list of URLs (repos:\\n  - https://...), a single string, or absent entirely.","commonSituations":"Porting a repos list from another monorepo tool (GitHub org list, meta repo config) that uses arrays; writing `repos: github.com/org` as a bare string.","solutions":["Convert the list to a mapping keyed by repo path with the URL as value.","Use stable relative paths as keys (these same strings must be used by links[].from/to).","Keep at least one entry: a group with zero repos still needs the mapping form."],"exampleFix":"# before (group.yaml)\nversion: 1\nname: my-group\nrepos:\n  - https://github.com/org/api\n  - https://github.com/org/web\n# -> Error: repos is required in group.yaml (must be a mapping)\n\n# after\nversion: 1\nname: my-group\nrepos:\n  services/api: https://github.com/org/api\n  services/web: https://github.com/org/web","handlingStrategy":"validation","validationCode":"import yaml from 'js-yaml';\n\nexport function requireReposMapping(content: string): void {\n  const doc = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as Record<string, unknown>;\n  const repos = doc.repos;\n  if (!repos || typeof repos !== 'object' || Array.isArray(repos)) {\n    throw new Error('group.yaml: `repos` must be a mapping of { repo-path: url }, not a list');\n  }\n}","typeGuard":"export const isReposMapping = (v: unknown): v is Record<string, string> =>\n  !!v && typeof v === 'object' && !Array.isArray(v) &&\n  Object.values(v).every((u) => typeof u === 'string');","tryCatchPattern":"try {\n  config = await loadGroupConfig(groupDir);\n} catch (err) {\n  if (err instanceof Error && err.message === 'repos is required in group.yaml (must be a mapping)') {\n    throw new Error(\n      'group.yaml repos must be key/value pairs:\\n  repos:\\n    services/api: https://github.com/org/api',\n      { cause: err },\n    );\n  }\n  throw err;\n}","preventionTips":["Author repos as path-to-URL mappings; keys become the canonical repo paths used everywhere else in the file.","Convert array-style repo lists from other tools before pasting them in.","Prefer short stable keys (services/api) over absolute paths — every links entry must repeat them exactly."],"tags":["yaml","configuration","validation","group","repos","missing-field"],"backgroundTag":"missing-required-config-field","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"}