JuliusBrussee/caveman · error

Hermes model provider/base_url keys are duplicated…

Error message

Hermes model provider/base_url keys are duplicated; refusing ambiguous install

What it means

When installing the Hermes native route block, the installer collects existing `provider:` and `base_url:` lines under the `model:` section so it can replace them. If the same key appears more than once in that section, the effective value is ambiguous (duplicates by key name), so the installer refuses the install rather than produce an ambiguous config.

Solutions

  1. Edit the Hermes config's `model:` section so `provider:` and `base_url:` each appear exactly once with your desired values.
  2. Re-run the Caveman enable command after de-duplicating the keys.
  3. Run `caveman doctor hermes` first if a stale Caveman route block contributed the duplicates.

Example fix

// before
model:
  provider: "anthropic"
  base_url: https://api.anthropic.com
  provider: "openai"
// after
model:
  provider: "anthropic"
  base_url: https://api.anthropic.com
Defensive patterns

Strategy: validation

Validate before calling

const lines = readFileSync(hermesCfg, 'utf8').split('\n');
const keys = lines.flatMap((l, i) => /^  (provider|base_url):/.test(l) ? [l.match(/^  ([^:]+):/)[1]] : []);
if (new Set(keys).size !== keys.length) {
  // dedupe provider/base_url keys in the model: section before enabling
}

Try / catch

try {
  await cavemanEnableHermes();
} catch (e) {
  if (String(e.message).includes('duplicated')) {
    dedupeModelKeys(hermesCfg, ['provider', 'base_url']);
    await cavemanEnableHermes();
  }
}

Prevention

When it happens

Trigger: Running the Hermes enable flow when the `model:` section of the Hermes YAML config contains duplicate `provider:` or duplicate `base_url:` keys (e.g. two `provider:` lines at the two-space indent level).

Common situations: Hand-edited configs where a provider/base_url pair was appended while the original remained; merge conflicts keeping both lines; pasted snippets from docs duplicating existing keys.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/9e449e168cb14b05. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:7747

  for (const marker of [HERMES_NATIVE_ROUTE_BEGIN, HERMES_NATIVE_ROUTE_END, HERMES_NATIVE_PLUGIN_BEGIN, HERMES_NATIVE_PLUGIN_END, HERMES_NATIVE_MCP_BEGIN, HERMES_NATIVE_MCP_END]) {
    if (source.includes(marker)) throw new Error("existing Hermes Caveman native block is unjournaled; run `caveman doctor hermes`");
  }
  const lines = yamlLines(source);
  let model = topLevelSection(lines, "model");
  if (!model) {
    lines.unshift("model:");
    model = topLevelSection(lines, "model")!;
  }
  const routeIndexes: number[] = [];
  const previousRouteLines: string[] = [];
  for (let i = model.start + 1; i < model.end; i++) {
    if (/^  (provider|base_url):/.test(lines[i]!)) {
      routeIndexes.push(i);
      previousRouteLines.push(lines[i]!);
    }
  }
  if (new Set(routeIndexes.map((index) => lines[index]!.match(/^  ([^:]+):/)?.[1])).size !== routeIndexes.length) {
    throw new Error("Hermes model provider/base_url keys are duplicated; refusing ambiguous install");
  }
  const insertAt = routeIndexes[0] ?? model.start + 1;
  for (const index of [...routeIndexes].sort((a, b) => b - a)) lines.splice(index, 1);
  const routeBlock = [
    `  ${HERMES_NATIVE_ROUTE_BEGIN}`,
    '  provider: "custom"',
    `  base_url: ${yamlQuote(appendUrlPath(gw, "/w/hermes/v1"))}`,
    `  ${HERMES_NATIVE_ROUTE_END}`,
  ];
  lines.splice(insertAt, 0, ...routeBlock);

  let pluginBlock: string | null = null;
  const plugins = topLevelSection(lines, "plugins");
  if (plugins) {
    let enabled = -1;
    for (let i = plugins.start + 1; i < plugins.end; i++) {
      if (/^  enabled:\s*\[/.test(lines[i]!)) throw new Error("Hermes plugins.enabled uses inline YAML; refusing unsafe native merge");
      if (/^  enabled:\s*(?:#.*)?$/.test(lines[i]!)) { enabled = i; break; }

View on GitHub (pinned to 3ee70a1026)