{"record":{"id":"9e449e168cb14b05","repo":"JuliusBrussee/caveman","slug":"hermes-model-provider-base-url-keys-are-duplicated","errorCode":null,"errorMessage":"Hermes model provider/base_url keys are duplicated; refusing ambiguous install","messagePattern":"Hermes model provider/base_url keys are duplicated; refusing ambiguous install","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":6709,"sourceCode":"  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]) {\n    if (source.includes(marker)) throw new Error(\"existing Hermes Caveman native block is unjournaled; run `caveman doctor hermes`\");\n  }\n  const lines = yamlLines(source);\n  let model = topLevelSection(lines, \"model\");\n  if (!model) {\n    lines.unshift(\"model:\");\n    model = topLevelSection(lines, \"model\")!;\n  }\n  const routeIndexes: number[] = [];\n  const previousRouteLines: string[] = [];\n  for (let i = model.start + 1; i < model.end; i++) {\n    if (/^  (provider|base_url):/.test(lines[i]!)) {\n      routeIndexes.push(i);\n      previousRouteLines.push(lines[i]!);\n    }\n  }\n  if (new Set(routeIndexes.map((index) => lines[index]!.match(/^  ([^:]+):/)?.[1])).size !== routeIndexes.length) {\n    throw new Error(\"Hermes model provider/base_url keys are duplicated; refusing ambiguous install\");\n  }\n  const insertAt = routeIndexes[0] ?? model.start + 1;\n  for (const index of [...routeIndexes].sort((a, b) => b - a)) lines.splice(index, 1);\n  const routeBlock = [\n    `  ${HERMES_NATIVE_ROUTE_BEGIN}`,\n    '  provider: \"custom\"',\n    `  base_url: ${yamlQuote(appendUrlPath(gw, \"/w/hermes\"))}`,\n    `  ${HERMES_NATIVE_ROUTE_END}`,\n  ];\n  lines.splice(insertAt, 0, ...routeBlock);\n\n  let pluginBlock: string | null = null;\n  const plugins = topLevelSection(lines, \"plugins\");\n  if (plugins) {\n    let enabled = -1;\n    for (let i = plugins.start + 1; i < plugins.end; i++) {\n      if (/^  enabled:\\s*\\[/.test(lines[i]!)) throw new Error(\"Hermes plugins.enabled uses inline YAML; refusing unsafe native merge\");\n      if (/^  enabled:\\s*(?:#.*)?$/.test(lines[i]!)) { enabled = i; break; }","sourceCodeStart":6691,"sourceCodeEnd":6727,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L6691-L6727","documentation":"hermesNativeConfig() replaces existing `provider:`/`base_url:` keys inside the top-level `model:` section with its own marked block. It first collects those lines; if normalizing the key names yields duplicates (e.g. provider listed twice), removal order and which value wins become ambiguous, so it refuses the install.","triggerScenarios":"Native Hermes install when the `model:` section contains the same key twice among provider/base_url (e.g. two `  provider:` lines, or provider plus base_url plus a second provider).","commonSituations":"Append-style edits to the Hermes config; pasted examples onto existing config; merge conflicts leaving duplicate keys.","solutions":["Open the Hermes config, find the model: section, and keep exactly one provider: and one base_url: line","Re-run the caveman hermes install"],"exampleFix":"# before\nmodel:\n  provider: \"openai\"\n  provider: \"custom\"\n  base_url: \"https://old.example\"\n\n# after\nmodel:\n  provider: \"openai\"\n  base_url: \"https://old.example\"","handlingStrategy":"validation","validationCode":"function hermesModelKeysUnique(lines: string[], section: {start: number, end: number}): boolean {\n  const names = [];\n  for (let i = section.start + 1; i < section.end; i++) {\n    const m = lines[i]!.match(/^  ([^:]+):/);\n    if (m && /^(provider|base_url):/.test(lines[i]!)) names.push(m[1]);\n  }\n  return new Set(names).size === names.length;\n}","typeGuard":null,"tryCatchPattern":"try { nativeInstallHermes(); } catch (e) {\n  if (e instanceof Error && /provider\\/base_url keys are duplicated/.test(e.message)) {\n    dedupeHermesModelKeys(); nativeInstallHermes();\n  } else throw e;\n}","preventionTips":["Keep exactly one provider: and one base_url: line under model:","Deduplicate after dotfile merges before running installers","Lint the Hermes YAML for duplicate keys"],"tags":["hermes","yaml","duplicate-keys","merge-safety"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}