abhigyanpatwari/GitNexus · error
repos is required in group.yaml (must be a mapping)
Error message
repos is required in group.yaml (must be a mapping)
What it means
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.
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.
Example fix
# before (group.yaml) version: 1 name: my-group repos: - https://github.com/org/api - https://github.com/org/web # -> Error: repos is required in group.yaml (must be a mapping) # after version: 1 name: my-group repos: services/api: https://github.com/org/api services/web: https://github.com/org/web
Defensive patterns
Strategy: validation
Validate before calling
import yaml from 'js-yaml';
export function requireReposMapping(content: string): void {
const doc = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as Record<string, unknown>;
const repos = doc.repos;
if (!repos || typeof repos !== 'object' || Array.isArray(repos)) {
throw new Error('group.yaml: `repos` must be a mapping of { repo-path: url }, not a list');
}
} Type guard
export const isReposMapping = (v: unknown): v is Record<string, string> => !!v && typeof v === 'object' && !Array.isArray(v) && Object.values(v).every((u) => typeof u === 'string');
Try / catch
try {
config = await loadGroupConfig(groupDir);
} catch (err) {
if (err instanceof Error && err.message === 'repos is required in group.yaml (must be a mapping)') {
throw new Error(
'group.yaml repos must be key/value pairs:\n repos:\n services/api: https://github.com/org/api',
{ cause: err },
);
}
throw err;
} Prevention
- 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.
When it happens
Trigger: Loading a group.yaml where repos is a YAML list of URLs (repos:\n - https://...), a single string, or absent entirely.
Common situations: 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.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- links[ ].contract is required
- name is required in group.yaml
- version is required in group.yaml
- Invalid YAML: expected an object
- links[ ].from " " does not match any repo path in group
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-09-01).
Data as JSON: /api/errors/f5b458fedc02b502.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/group/config-parser.ts:60
const DEFAULT_MATCHING = {
exclude_links_paths: [] as string[],
exclude_links_param_only_paths: false,
};
export function parseGroupConfig(yamlContent: string): GroupConfig {
const raw = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA }) as Record<string, unknown>;
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
throw new Error('Invalid YAML: expected an object');
}
if (raw.version === undefined) throw new Error('version is required in group.yaml');
if (raw.version !== 1) {
throw new Error(`Unsupported group.yaml version: ${raw.version}. Expected 1.`);
}
if (!raw.name || typeof raw.name !== 'string') throw new Error('name is required in group.yaml');
if (!raw.repos || typeof raw.repos !== 'object' || Array.isArray(raw.repos)) {
throw new Error('repos is required in group.yaml (must be a mapping)');
}
const reposRaw = raw.repos as Record<string, unknown>;
const repos: Record<string, string> = {};
for (const [memberPath, registryName] of Object.entries(reposRaw)) {
if (typeof registryName !== 'string' || registryName.trim() === '') {
throw new Error(
`repos["${memberPath}"] must be a non-empty registry name string, not ${typeof registryName}`,
);
}
repos[memberPath] = registryName.trim();
}
const repoPaths = new Set(Object.keys(repos));
const rawLinks = (raw.links as unknown[]) || [];
const links: GroupManifestLink[] = rawLinks.map((l: unknown, i: number) => {
const link = l as Record<string, unknown>;
if (!link.from || !repoPaths.has(link.from as string)) {View on GitHub (pinned to 52924ef12c)