abhigyanpatwari/GitNexus · error · GitNexusRcError

: " " and " " both configure the same option; set only one.

Error message

${GITNEXUS_RC_FILENAME}: "${prev}" and "${key}" both configure the same option; set only one.

What it means

Several .gitnexusrc keys are aliases for the same underlying AnalyzeOptions target (branch→defaultBranch, skipContextFiles/skipAiContext→skipAgentsMd, noStats→stats). normalizeLevel tracks which physical key set each target; if two different aliases both appear at the same level, the config is ambiguous and rejected.

Solutions

  1. Delete one of the two aliases named in the message — the error quotes both keys explicitly
  2. Standardize the team's .gitnexusrc on one alias set and enforce it in review
  3. If you need per-command override, put the base value flat and the override in the nested "analyze" object (nested legitimately overrides flat)

Example fix

// before (.gitnexusrc)
{"defaultBranch": "main", "branch": "develop"}
// after
{"defaultBranch": "develop"}
Defensive patterns

Strategy: validation

Validate before calling

const ALIAS_GROUPS: string[][] = [
  ['defaultBranch', 'branch'],
  ['skipAgentsMd', 'skipContextFiles', 'skipAiContext'],
  ['stats', 'noStats'],
];
const keys = new Set(Object.keys(rc));
for (const group of ALIAS_GROUPS) {
  const present = group.filter((k) => keys.has(k));
  if (present.length > 1) throw new Error(`Pick one of: ${present.join(', ')}`);
}

Try / catch

catch (err) {
  if (err instanceof GitNexusRcError && err.message.includes('both configure the same option')) {
    // message names both keys; delete one of them
  }
}

Prevention

When it happens

Trigger: {"defaultBranch": "main", "branch": "develop"}; {"skipAgentsMd": true, "skipContextFiles": true}; {"stats": true, "noStats": true} — each pair collides on one target option; applies inside the nested "analyze" block too (flat vs nested across levels is allowed, nested wins).

Common situations: Merging rc files from two teams that each standardized on a different alias; incremental edits adding the newer alias without removing the old one; docs examples that mix aliases in one snippet.

Related errors


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

Appendix: source

Thrown at gitnexus/src/cli/analyze-config.ts:344

): Partial<AnalyzeOptions> => {
  const out: Partial<AnalyzeOptions> = {};
  const setBy = new Map<keyof AnalyzeOptions, string>();

  for (const [key, value] of Object.entries(obj)) {
    if (allowNestedKey && key === NESTED_KEY) continue; // handled separately
    // `Object.hasOwn`, not a truthiness check: a plain-object lookup like
    // `KEY_SPECS["__proto__"]` returns an inherited member (Object.prototype,
    // truthy) and would slip past `if (!spec)`, hitting the wrong error branch
    // instead of the documented "Unknown key" message (#1996 tri-review P3).
    if (!Object.hasOwn(KEY_SPECS, key)) {
      throw new GitNexusRcError(
        `Unknown key "${key}" in ${GITNEXUS_RC_FILENAME}. ${ALLOWED_KEYS_HINT}`,
      );
    }
    const spec = KEY_SPECS[key];
    const prev = setBy.get(spec.target);
    if (prev && prev !== key) {
      throw new GitNexusRcError(
        `${GITNEXUS_RC_FILENAME}: "${prev}" and "${key}" both configure the same option; set only one.`,
      );
    }
    setBy.set(spec.target, key);
    (out as Record<string, unknown>)[spec.target] = normalizeValue(spec.kind, value, key);
  }

  return out;
};

/**
 * Locate, read, parse, validate, and normalize `.gitnexusrc` at `repoRoot`.
 *
 * @returns the normalized config defaults, or `undefined` when no file exists
 *          (the normal case). Throws {@link GitNexusRcError} on any problem.
 */
export function loadAnalyzeConfig(repoRoot: string): Partial<AnalyzeOptions> | undefined {
  const filePath = path.join(repoRoot, GITNEXUS_RC_FILENAME);

View on GitHub (pinned to ac9a4e9abd)