abhigyanpatwari/GitNexus · error · Error

links[ ].role " " is invalid. Expected: provider | consumer

Error message

links[${i}].role "${link.role}" is invalid. Expected: provider | consumer

What it means

Thrown while validating a group.yaml links entry: the link's `role` is not exactly 'provider' or 'consumer' — the only two valid roles. Role records which side of the contract this link describes (the repo in `from` exposing vs consuming the interface), which the contract matching step relies on.

Solutions

  1. Set role to exactly `provider` or `consumer` (lowercase, singular), describing the `from` repo's relation to the contract.
  2. For a from-repo that implements/exposes the interface use provider; for one that calls it use consumer.
  3. Check every link — the validator stops at the first bad entry, so fix iteratively or lint the whole file.

Example fix

# before (group.yaml)
links:
  - from: services/api
    to: services/web
    type: http
    role: server
# -> Error: links[0].role "server" is invalid. Expected: provider | consumer

# after
links:
  - from: services/api
    to: services/web
    type: http
    role: provider
Defensive patterns

Strategy: validation

Validate before calling

import yaml from 'js-yaml';

const VALID_ROLES = ['provider', 'consumer'];

export function validateLinkRoles(content: string): void {
  const doc = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as Record<string, any>;
  (doc.links ?? []).forEach((link: any, i: number) => {
    if (!VALID_ROLES.includes(link?.role)) {
      throw new Error(
        `links[${i}].role must be 'provider' or 'consumer' (describing the \`from\` repo) — got ${JSON.stringify(link?.role)}`,
      );
    }
  });
}

Type guard

export const isContractRole = (r: unknown): r is 'provider' | 'consumer' =>
  r === 'provider' || r === 'consumer';

Try / catch

try {
  config = await loadGroupConfig(groupDir);
} catch (err) {
  if (err instanceof Error && /links\[\d+\]\.role .* is invalid/.test(err.message)) {
    throw new Error(
      'link role must be exactly provider or consumer (lowercase), describing the from-repo',
      { cause: err },
    );
  }
  throw err;
}

Prevention

When it happens

Trigger: A links[i].role that is missing, misspelled (providor, consumes), pluralized (providers), or uses a different vocabulary (server, client).

Common situations: Thinking role describes the `to` repo and writing client/server; auto-completing to provider/consumer variants; omitting role on some links while copying others.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20). Data as JSON: /api/errors/e91e656f3b94edcb. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/core/group/config-parser.ts:90

  }
  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`);
    }
    return {
      from: link.from as string,
      to: link.to as string,
      type: link.type as ManifestContractType,
      contract: String(link.contract),
      role: link.role as ContractRole,
    };
  });

  const rawDetect = raw.detect;

View on GitHub (pinned to 52924ef12c)