{"record":{"id":"f318d5cf1048122c","repo":"JuliusBrussee/caveman","slug":"aider-config-has-duplicate-read-keys-refusing-uns","errorCode":null,"errorMessage":"Aider config has duplicate read keys; refusing unsafe merge","messagePattern":"Aider config has duplicate read keys; refusing unsafe merge","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7426,"sourceCode":"function 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;\n    while (end < lines.length && !/^[A-Za-z0-9][A-Za-z0-9_-]*\\s*:/.test(lines[end]!)) end++;\n    readBlockLines = [`  ${AIDER_NATIVE_READ_BEGIN}`, `  - ${yamlQuote(corePath)}`, `  ${AIDER_NATIVE_READ_END}`];\n    lines.splice(end, 0, ...readBlockLines);\n  } else {\n    if (lines.some((line) => line.trim() !== \"\") && lines[lines.length - 1]?.trim()) lines.push(\"\");\n    readBlockLines = [AIDER_NATIVE_READ_BEGIN, \"read:\", `  - ${yamlQuote(corePath)}`, AIDER_NATIVE_READ_END];\n    lines.push(...readBlockLines);\n  }\n  const readBlock = readBlockLines.join(newline);\n  const text = lines.join(newline).replace(/(?:\\r?\\n)+$/, \"\") + newline;\n  return { text, routeBlock, previousRouteLine, readBlock };","sourceCodeStart":7408,"sourceCodeEnd":7444,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L7408-L7444","documentation":"aiderNativeConfig() finds the `read:` key to inject the Caveman core file into Aider's read list. If the config contains more than one `read:` key, the block-list target is ambiguous and the installer refuses the unsafe merge rather than editing the wrong section.","triggerScenarios":"Enabling Caveman for Aider when ~/.aider.conf.yml has two or more top-level `read:` lines, e.g. one block-list `read:` plus another `read:` line appended later.","commonSituations":"Hand-edited configs where a second `read:` key was pasted in; merge conflicts that kept both occurrences; documentation snippets added without checking existing keys.","solutions":["Consolidate ~/.aider.conf.yml to a single `read:` key with one block list containing all desired paths.","Re-run the Caveman enable command after fixing the config.","Run `caveman doctor aider` if a stale Caveman read block contributed to the duplication."],"exampleFix":"// before\nread:\n  - foo.md\nread:\n  - bar.md\n// after\nread:\n  - foo.md\n  - bar.md","handlingStrategy":"validation","validationCode":"const lines = readFileSync(aiderCfg, 'utf8').split('\\n');\nconst n = lines.filter((l) => /^read\\s*:/.test(l)).length;\nif (n > 1) throw new Error('consolidate to a single read: key before enabling');","typeGuard":null,"tryCatchPattern":"try {\n  await cavemanEnableAider();\n} catch (e) {\n  if (String(e.message).includes('duplicate read keys')) {\n    mergeReadBlocks(aiderCfg);\n    await cavemanEnableAider();\n  }\n}","preventionTips":["Use a single `read:` block list in ~/.aider.conf.yml.","Append new files to the existing read list rather than adding a new key.","Check for existing keys before pasting documentation snippets.","Validate the YAML for duplicate keys before enabling."],"tags":["aider","config","yaml","duplicate-keys"],"backgroundTag":"conflicting-config-options","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}