JuliusBrussee/caveman · error
Aider Caveman config block changed after enable; refusing…
Error message
Aider Caveman config block changed after enable; refusing destructive disable
What it means
When rolling back the Aider config, the tool verifies both journaled blocks are still present verbatim in the file. If route_block or read_block is no longer found in the text, the file was modified after enable and the tool refuses to disable destructively rather than producing a broken config.
Solutions
- Restore the Caveman blocks to their exact journaled content (or restore the file from backup/git), then re-run disable.
- Manually delete the Caveman blocks from the Aider config instead of using disable.
- Re-run enable to overwrite the blocks and refresh the journal, then disable.
Example fix
// before: block edited after enable # caveman-start model: openrouter/x # changed line # caveman-end // after: exact original block restored # caveman-start model: openrouter/caveman # caveman-end
Defensive patterns
Strategy: validation
Validate before calling
const text = fs.readFileSync(aiderConfig, 'utf8');
const { route_block: rb, read_block: db } = journal.owned ?? {};
if (typeof rb !== 'string' || typeof db !== 'string' || !text.includes(rb) || !text.includes(db)) {
throw new Error('Aider config drift detected; restore blocks before disable');
} Type guard
const blocksIntact = (text, owned) => typeof owned?.route_block === 'string' && text.includes(owned.route_block) && typeof owned?.read_block === 'string' && text.includes(owned.read_block);
Try / catch
try {
await caveman.disable(['aider']);
} catch (err) {
if (err.message.includes('Aider Caveman config block changed')) {
console.error('Restore .aider.conf.yml blocks (git checkout) or remove them manually.');
} else throw err;
} Prevention
- Commit or back up .aider.conf.yml before enabling.
- Don't edit lines inside the marked Caveman blocks.
- Run formatters that preserve block contents, or exclude the config from reformatting.
- If a manual edit happened, re-run enable to refresh the journal before disable.
When it happens
Trigger: Running disable with kind === "aider-config" when text.includes(routeBlock) or text.includes(readBlock) is false — the user (or a formatter/another tool) edited, reformatted, or partially removed the Caveman blocks in the Aider config after enable.
Common situations: Manual cleanup of .aider.conf.yml that changed whitespace/comments inside the block; reformatting tool collapsed lines; user commented the block out.
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 openai-api-base keys; refusing…
- Aider config has duplicate read keys; refusing unsafe merge
- Aider config uses inline/scalar read; use block-list form…
- Aider integration journal lacks owned blocks
- existing Aider Caveman block is unjournaled; run `caveman…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/10910b95a8faaa5d.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:8391
}
if (mcp.caveman !== undefined) {
if (operation.owned?.previous_mcp === null || operation.owned?.previous_mcp === undefined) delete mcp.caveman;
else mcp.caveman = operation.owned.previous_mcp;
}
if (Object.keys(mcp).length > 0) root.mcp = mcp;
else delete root.mcp;
return jsonBytes(root);
}
if (operation.kind === "aider-config") {
let text = current.toString("utf8");
const routeBlock = operation.owned?.route_block;
const readBlock = operation.owned?.read_block;
const previousRouteLine = operation.owned?.previous_route_line;
if (typeof routeBlock !== "string" || typeof readBlock !== "string") {
throw new Error("Aider integration journal lacks owned blocks");
}
if (!text.includes(routeBlock) || !text.includes(readBlock)) {
throw new Error("Aider Caveman config block changed after enable; refusing destructive disable");
}
text = text.replace(routeBlock, typeof previousRouteLine === "string" ? previousRouteLine : "");
text = text.replace(readBlock, "");
return Buffer.from(text.replace(/\n{3,}/g, "\n\n"));
}
if (operation.kind === "aider-core") {
throw new Error(`${operation.file} changed after enable; refusing destructive disable`);
}
if (operation.kind === "hermes-plugin-manifest" || operation.kind === "hermes-plugin-init") {
throw new Error(`${operation.file} changed after enable; refusing destructive disable`);
}
if (operation.kind === "hermes-config") {
let text = current.toString("utf8");
const routeBlock = operation.owned?.route_block;
const previousRouteLines = operation.owned?.previous_route_lines;
if (typeof routeBlock !== "string" || !Array.isArray(previousRouteLines)) {
throw new Error("Hermes integration journal lacks owned routing block");
}View on GitHub (pinned to 3ee70a1026)