abhigyanpatwari/GitNexus · error · Error
links[ ].from " " does not match any repo path in group
Error message
links[${i}].from "${link.from}" does not match any repo path in group What it means
Thrown while validating each entry of group.yaml's optional `links` list: the link's `from` value is missing or is not one of the keys of the `repos` mapping. Links may only connect repos declared in the same manifest, so an unknown or misspelled path is rejected before the link graph is built.
Solutions
- Copy the exact key from the repos mapping into links[i].from (the error echoes the offending value for comparison).
- If the repo was renamed, update every links[].from/to that referenced the old key, not just repos.
- Use one path convention (plain relative keys, no ./ prefix, no trailing slash) throughout the file.
Example fix
# before (group.yaml)
repos:
services/api: https://github.com/org/api
links:
- from: ./services/api
to: services/web
# -> Error: links[0].from "./services/api" does not match any repo path in group
# after
links:
- from: services/api
to: services/web Defensive patterns
Strategy: validation
Validate before calling
import yaml from 'js-yaml';
// Validate link endpoints against repos keys BEFORE loadGroupConfig.
export function validateLinkRefs(content: string): void {
const doc = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as Record<string, any>;
const repoPaths = new Set(Object.keys(doc.repos ?? {}));
(doc.links ?? []).forEach((link: any, i: number) => {
for (const side of ['from', 'to'] as const) {
if (!repoPaths.has(link?.[side])) {
throw new Error(
`links[${i}].${side} ${JSON.stringify(link?.[side])} is not a repos key ` +
`(known: ${[...repoPaths].join(', ')})`,
);
}
}
});
} Type guard
export const isKnownRepoPath = (p: unknown, repoPaths: Set<string>): p is string => typeof p === 'string' && repoPaths.has(p);
Try / catch
try {
config = await loadGroupConfig(groupDir);
} catch (err) {
const m = /^links\[(\d+)\]\.(from|to) (.*) does not match/.exec(
err instanceof Error ? err.message : '',
);
if (m) {
throw new Error(
`group.yaml links[${m[1]}].${m[2]}: copy the exact repos key (check typo, ./ prefix, trailing slash)`,
{ cause: err },
);
}
throw err;
} Prevention
- Copy-paste repo keys from the repos block instead of retyping them in links.
- Adopt one path style (no ./ prefix, no trailing slash) and enforce it in review.
- When renaming a repos key, grep the file for the old value — every links from/to referencing it must be updated.
When it happens
Trigger: A links[i].from value that differs from a repos key: typo (servcies/api vs services/api), absolute vs relative path style, a trailing slash or ./ prefix mismatch, or a repo that was removed from repos while its link remained.
Common situations: Renaming a repo path in repos without updating the links that reference it; copying a link block between group.yaml files of different groups; mixing path conventions (./services/api vs services/api).
Related errors
- links[ ].to " " does not match any repo path in group
- links[ ].contract is required
- links[ ].role " " is invalid. Expected: provider | consumer
- links[ ].type " " is invalid. Expected
- Invalid YAML: expected an object
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20).
Data as JSON: /api/errors/35cdf36a51b5fac6.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/group/config-parser.ts:79
}
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)) {
throw new Error(`links[${i}].from "${link.from}" does not match any repo path in group`);
}
if (!link.to || !repoPaths.has(link.to as string)) {
throw new Error(`links[${i}].to "${link.to}" does not match any repo path in group`);
}
if (!VALID_CONTRACT_TYPES.includes(link.type as ManifestContractType)) {
throw new Error(
`links[${i}].type "${link.type}" is invalid. Expected: ${VALID_CONTRACT_TYPES.join(', ')}`,
);
}
if (!VALID_ROLES.includes(link.role as ContractRole)) {
throw new Error(`links[${i}].role "${link.role}" is invalid. Expected: provider | consumer`);
}
if (
link.contract === undefined ||
link.contract === null ||
String(link.contract).trim() === ''
) {
throw new Error(`links[${i}].contract is required`);View on GitHub (pinned to 52924ef12c)