{"record":{"id":"c665e133bcce9da9","repo":"JuliusBrussee/caveman","slug":"aider-config-has-duplicate-openai-api-base-keys-r","errorCode":null,"errorMessage":"Aider config has duplicate openai-api-base keys; refusing unsafe merge","messagePattern":"Aider config has duplicate openai-api-base keys; refusing unsafe merge","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":6386,"sourceCode":"  ];\n}\n\nfunction aiderCorePath(): string {\n  return join(cavemanHome(), \"packs\", \"aider\", \"CAVEMAN.md\");\n}\n\nfunction aiderCoreSource(): string {\n  return `${AIDER_NATIVE_CORE_MARKER}\\n${NATIVE_CORE}\\n\\nHost limits: Aider repository map remains authoritative. Caveman observes provider traffic through local proxy; no pre-tool governance or lifecycle interception is active.\\n`;\n}\n\nfunction aiderNativeConfig(source: string, route: string, corePath: string): { text: string; routeBlock: string; previousRouteLine: string | null; readBlock: string } {\n  if ([AIDER_NATIVE_ROUTE_BEGIN, AIDER_NATIVE_ROUTE_END, AIDER_NATIVE_READ_BEGIN, AIDER_NATIVE_READ_END].some((marker) => source.includes(marker))) {\n    throw new Error(\"existing Aider Caveman block is unjournaled; run `caveman doctor aider`\");\n  }\n  const newline = source.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n  const lines = source.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\n  const routeIndexes = lines.flatMap((line, index) => /^openai-api-base\\s*:/.test(line) ? [index] : []);\n  if (routeIndexes.length > 1) throw new Error(\"Aider config has duplicate openai-api-base keys; refusing unsafe merge\");\n  const routeBlockLines = [AIDER_NATIVE_ROUTE_BEGIN, `openai-api-base: ${yamlQuote(route)}`, AIDER_NATIVE_ROUTE_END];\n  const routeBlock = routeBlockLines.join(newline);\n  const previousRouteLine = routeIndexes.length === 1 ? lines[routeIndexes[0]!]! : null;\n  if (routeIndexes.length === 1) lines.splice(routeIndexes[0]!, 1, ...routeBlockLines);\n  else {\n    if (lines.some((line) => line.trim() !== \"\") && lines[lines.length - 1]?.trim()) lines.push(\"\");\n    lines.push(...routeBlockLines);\n  }\n\n  const readIndexes = lines.flatMap((line, index) => /^read\\s*:/.test(line) ? [index] : []);\n  if (readIndexes.length > 1) throw new Error(\"Aider config has duplicate read keys; refusing unsafe merge\");\n  let readBlockLines: string[];\n  if (readIndexes.length === 1) {\n    const readIndex = readIndexes[0]!;\n    if (!/^read\\s*:\\s*(?:#.*)?$/.test(lines[readIndex]!)) {\n      throw new Error(\"Aider config uses inline/scalar read; use block-list form before enabling Caveman\");\n    }\n    let end = readIndex + 1;","sourceCodeStart":6368,"sourceCodeEnd":6404,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L6368-L6404","documentation":"aiderNativeConfig() must replace or wrap the single `openai-api-base:` key in ~/.aider.conf.yml to point Aider at the Caveman route. YAML top-level keys should be unique; if more than one openai-api-base line exists, replacing one would leave ambiguous routing, so it refuses the merge.","triggerScenarios":"Native Aider install when ~/.aider.conf.yml contains two or more lines matching /^openai-api-base\\s*:/ (duplicate top-level keys).","commonSituations":"Append-style edits that added a new base URL without removing the old one; dotfile merges/conflicts; copy-pasting examples onto an existing config.","solutions":["Open ~/.aider.conf.yml, find all openai-api-base lines (`grep -n 'openai-api-base' ~/.aider.conf.yml`), keep one, delete the rest","Retry the caveman install for aider"],"exampleFix":"# before — ~/.aider.conf.yml\nopenai-api-base: https://api.openai.com/v1\nopenai-api-base: https://old-proxy.example/v1\n\n# after\nopenai-api-base: https://api.openai.com/v1","handlingStrategy":"validation","validationCode":"import { readFileSync } from \"node:fs\";\nfunction aiderNoDuplicateRoute(path: string): boolean {\n  try { return readFileSync(path, \"utf8\").split(/\\r?\\n/).filter((l) => /^openai-api-base\\s*:/.test(l)).length <= 1; }\n  catch { return true; }\n}","typeGuard":null,"tryCatchPattern":"try { nativeInstallAider(); } catch (e) {\n  if (e instanceof Error && /duplicate openai-api-base/.test(e.message)) {\n    dedupeAiderKey(\"openai-api-base\"); nativeInstallAider();\n  } else throw e;\n}","preventionTips":["Replace, don't append, when changing openai-api-base in .aider.conf.yml","Lint YAML configs for duplicate top-level keys (yamllint) before running installers","Resolve dotfile merge conflicts fully instead of keeping both sides"],"tags":["aider","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"}