JuliusBrussee/caveman · error
Aider config has duplicate openai-api-base keys; refusing…
Error message
Aider config has duplicate openai-api-base keys; refusing unsafe merge
What it means
aiderNativeConfig() locates the `openai-api-base` key in ~/.aider.conf.yml to replace it with the Caveman route block. If the config declares that key more than once, YAML semantics make the effective value ambiguous, so the installer refuses the merge instead of guessing which key to replace.
Solutions
- Open ~/.aider.conf.yml and delete the duplicate `openai-api-base:` lines, keeping exactly one with your desired value.
- Re-run the Caveman enable command after the config has a single `openai-api-base` key.
- If a previous Caveman route block caused the duplication, run `caveman doctor aider` to strip stale blocks first.
Example fix
// before openai-api-base: https://api.openai.com/v1 openai-api-base: http://localhost:4000/v1 // after openai-api-base: https://api.openai.com/v1
Defensive patterns
Strategy: validation
Validate before calling
const lines = readFileSync(aiderCfg, 'utf8').split('\n');
const n = lines.filter((l) => /^openai-api-base\s*:/.test(l)).length;
if (n > 1) throw new Error('fix duplicate openai-api-base keys before enabling'); Try / catch
try {
await cavemanEnableAider();
} catch (e) {
if (String(e.message).includes('duplicate openai-api-base')) {
dedupeKey(aiderCfg, 'openai-api-base');
await cavemanEnableAider();
}
} Prevention
- Keep one `openai-api-base` key in ~/.aider.conf.yml.
- When pasting config snippets, search the file for the key first.
- Resolve merge conflicts by deduplicating keys, not keeping both lines.
- Lint YAML configs for duplicate top-level keys before enabling.
When it happens
Trigger: Enabling Caveman for Aider when ~/.aider.conf.yml contains two or more top-level `openai-api-base:` lines (e.g. one manually added and one appended by a previous edit).
Common situations: Users appending config lines at the end of the file without noticing an existing key earlier in the file; merge conflicts resolved by keeping both lines; copied config snippets pasted over an existing key.
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
- Aider config has duplicate read keys; refusing unsafe merge
- Aider config uses inline/scalar read; use block-list form…
- Hermes model provider/base_url keys are duplicated…
- Aider Caveman config block changed after enable; refusing…
- auth_token: in is ignored — the inbound token is read only…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/c665e133bcce9da9.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:7415
}];
}
function aiderCorePath(): string {
return join(cavemanHome(), "packs", "aider", "CAVEMAN.md");
}
function aiderCoreSource(): string {
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`;
}
function aiderNativeConfig(source: string, route: string, corePath: string): { text: string; routeBlock: string; previousRouteLine: string | null; readBlock: string } {
if ([AIDER_NATIVE_ROUTE_BEGIN, AIDER_NATIVE_ROUTE_END, AIDER_NATIVE_READ_BEGIN, AIDER_NATIVE_READ_END].some((marker) => source.includes(marker))) {
throw new Error("existing Aider Caveman block is unjournaled; run `caveman doctor aider`");
}
const newline = source.includes("\r\n") ? "\r\n" : "\n";
const lines = source.replace(/\r\n/g, "\n").split("\n");
const routeIndexes = lines.flatMap((line, index) => /^openai-api-base\s*:/.test(line) ? [index] : []);
if (routeIndexes.length > 1) throw new Error("Aider config has duplicate openai-api-base keys; refusing unsafe merge");
const routeBlockLines = [AIDER_NATIVE_ROUTE_BEGIN, `openai-api-base: ${yamlQuote(route)}`, AIDER_NATIVE_ROUTE_END];
const routeBlock = routeBlockLines.join(newline);
const previousRouteLine = routeIndexes.length === 1 ? lines[routeIndexes[0]!]! : null;
if (routeIndexes.length === 1) lines.splice(routeIndexes[0]!, 1, ...routeBlockLines);
else {
if (lines.some((line) => line.trim() !== "") && lines[lines.length - 1]?.trim()) lines.push("");
lines.push(...routeBlockLines);
}
const readIndexes = lines.flatMap((line, index) => /^read\s*:/.test(line) ? [index] : []);
if (readIndexes.length > 1) throw new Error("Aider config has duplicate read keys; refusing unsafe merge");
let readBlockLines: string[];
if (readIndexes.length === 1) {
const readIndex = readIndexes[0]!;
if (!/^read\s*:\s*(?:#.*)?$/.test(lines[readIndex]!)) {
throw new Error("Aider config uses inline/scalar read; use block-list form before enabling Caveman");
}
let end = readIndex + 1;View on GitHub (pinned to 3ee70a1026)