{"record":{"id":"10910b95a8faaa5d","repo":"JuliusBrussee/caveman","slug":"aider-caveman-config-block-changed-after-enable-r","errorCode":null,"errorMessage":"Aider Caveman config block changed after enable; refusing destructive disable","messagePattern":"Aider Caveman config block changed after enable; refusing destructive disable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7696,"sourceCode":"    }\n    if (mcp.caveman !== undefined) {\n      if (operation.owned?.previous_mcp === null || operation.owned?.previous_mcp === undefined) delete mcp.caveman;\n      else mcp.caveman = operation.owned.previous_mcp;\n    }\n    if (Object.keys(mcp).length > 0) root.mcp = mcp;\n    else delete root.mcp;\n    return jsonBytes(root);\n  }\n  if (operation.kind === \"aider-config\") {\n    let text = current.toString(\"utf8\");\n    const routeBlock = operation.owned?.route_block;\n    const readBlock = operation.owned?.read_block;\n    const previousRouteLine = operation.owned?.previous_route_line;\n    if (typeof routeBlock !== \"string\" || typeof readBlock !== \"string\") {\n      throw new Error(\"Aider integration journal lacks owned blocks\");\n    }\n    if (!text.includes(routeBlock) || !text.includes(readBlock)) {\n      throw new Error(\"Aider Caveman config block changed after enable; refusing destructive disable\");\n    }\n    text = text.replace(routeBlock, typeof previousRouteLine === \"string\" ? previousRouteLine : \"\");\n    text = text.replace(readBlock, \"\");\n    return Buffer.from(text.replace(/\\n{3,}/g, \"\\n\\n\"));\n  }\n  if (operation.kind === \"aider-core\") {\n    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);\n  }\n  if (operation.kind === \"hermes-plugin-manifest\" || operation.kind === \"hermes-plugin-init\") {\n    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);\n  }\n  if (operation.kind === \"hermes-config\") {\n    let text = current.toString(\"utf8\");\n    const routeBlock = operation.owned?.route_block;\n    const previousRouteLines = operation.owned?.previous_route_lines;\n    if (typeof routeBlock !== \"string\" || !Array.isArray(previousRouteLines)) {\n      throw new Error(\"Hermes integration journal lacks owned routing block\");\n    }","sourceCodeStart":7678,"sourceCodeEnd":7714,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/2f49f0e1a352aa810e70056b7930aeb0b3d219b4/packages/cli/src/index.ts#L7678-L7714","documentation":"Thrown by `caveman disable aider` when ~/.aider.conf.yml drifted after enable (hash differs from after_sha256) and the exact journaled route_block or read_block strings are no longer present verbatim in the file. Caveman only removes what it can match exactly; if its owned blocks were edited or reformatted, removal could cut into your content, so it refuses the destructive disable.","triggerScenarios":"Editing the Caveman routing or read-tool lines in ~/.aider.conf.yml after `caveman enable aider` — e.g. changing the annotated model/settings lines, reordering them, or a formatter normalizing YAML indentation — then running `caveman disable aider`.","commonSituations":"Tweaking the OpenAI base URL line Caveman added to point elsewhere; aider or an editor rewriting/reformatting the YAML; merging dotfile changes via git that touch the same lines.","solutions":["Restore the owned blocks to the exact strings in owned.route_block / owned.read_block of ~/.caveman/integrations/aider.json, then rerun `caveman disable aider`","Run `caveman doctor aider` first to see per-file drift, and reconcile only the reported lines","If the edits must stay: manually strip the Caveman blocks from ~/.aider.conf.yml and delete ~/.caveman/integrations/aider.json","Re-run `caveman enable aider` to re-journal the current content, then disable immediately"],"exampleFix":"# before: the caveman-owned block in ~/.aider.conf.yml was edited\n# >>> caveman:native-routing\nopenai-api-base: http://localhost:9999/v1   # changed from installed route\n\n# after: block matches owned.route_block byte-for-byte, then `caveman disable aider` succeeds","handlingStrategy":"try-catch","validationCode":"// Confirm the exact owned blocks still exist in ~/.aider.conf.yml before disable\nimport { readFileSync } from 'node:fs';\nconst home = process.env.CAVEMAN_HOME ?? `${process.env.HOME}/.caveman`;\nconst j = JSON.parse(readFileSync(`${home}/integrations/aider.json`, 'utf8'));\nconst text = readFileSync(`${process.env.HOME}/.aider.conf.yml`, 'utf8');\nfor (const op of j.operations) {\n  if (op.kind === 'aider-config' && (!text.includes(op.owned.route_block) || !text.includes(op.owned.read_block))) {\n    console.error('refusing: caveman blocks in .aider.conf.yml were edited');\n  }\n}","typeGuard":null,"tryCatchPattern":"catch (err) { if (err.message.includes('config block changed after enable')) { /* diff .aider.conf.yml against owned blocks in the journal; restore them verbatim; retry once */ } else throw err; }","preventionTips":["Keep caveman-added lines in .aider.conf.yml untouched; make custom edits outside the caveman blocks","Run `caveman doctor aider` before scripted disable","If you edit the routing lines by accident, re-run `caveman enable aider` to re-stamp"],"tags":["aider","config-drift","yaml","disable"],"backgroundTag":"config-drift-detected","analyzedSha":"2f49f0e1a352aa810e70056b7930aeb0b3d219b4","analyzedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}