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

  1. Restore the Caveman blocks to their exact journaled content (or restore the file from backup/git), then re-run disable.
  2. Manually delete the Caveman blocks from the Aider config instead of using disable.
  3. 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

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


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)