JuliusBrussee/caveman · error
Hermes routing block changed after enable; refusing…
Error message
Hermes routing block changed after enable; refusing destructive disable
What it means
During Hermes config disable, the journaled routing block must still exist verbatim in the config file. If route_block is not found in the text, the file was modified after enable and the tool refuses to disable destructively, protecting user edits from being clobbered by blind text replacement.
Solutions
- Restore the routing block to its exact journaled content (git checkout/backup), then re-run disable.
- Manually delete the Caveman routing block from the Hermes config.
- Re-run enable to rewrite the block and refresh the journal, then disable.
Example fix
// before: whitespace changed inside block # caveman-route-start route: caveman # reformatted # caveman-route-end // after: exact original restored # caveman-route-start route: caveman # caveman-route-end
Defensive patterns
Strategy: validation
Validate before calling
const text = fs.readFileSync(hermesConfig, 'utf8');
const rb = journal.owned?.route_block;
if (typeof rb !== 'string' || !text.includes(rb)) {
throw new Error('Hermes routing block drift detected; restore it before disable');
} Type guard
const routeBlockIntact = (text, owned) => typeof owned?.route_block === 'string' && text.includes(owned.route_block);
Try / catch
try {
await caveman.disable(['hermes']);
} catch (err) {
if (err.message.includes('Hermes routing block changed')) {
console.error('Restore the exact routing block (git checkout) or delete it manually.');
} else throw err;
} Prevention
- Commit the Hermes config before enable.
- Don't tweak routing rules inside the marked Caveman block; configure routing elsewhere.
- Watch for formatters/linters rewriting the config file.
- Re-run enable after any intentional change to refresh the journal.
When it happens
Trigger: Running disable with kind === "hermes-config" when !text.includes(routeBlock) — the routing block was edited, reformatted, commented out, or partially deleted after enable.
Common situations: User tweaked routing rules inside the Caveman block; a config formatter changed indentation/line endings; user already manually removed part of the block.
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
- Hermes block changed after enable; refusing destructive…
- Aider Caveman config block changed after enable; refusing…
- existing Hermes Caveman native block is unjournaled; run…
- Hermes integration journal lacks owned routing block
- Hermes model provider/base_url keys are duplicated…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/4ee7a775110a1fc0.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:8410
}
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");
}
if (!text.includes(routeBlock)) throw new Error("Hermes routing block changed after enable; refusing destructive disable");
text = text.replace(routeBlock, previousRouteLines.filter((line): line is string => typeof line === "string").join("\n"));
for (const key of ["plugin_block", "mcp_block"] as const) {
const block = operation.owned?.[key];
if (block === null || block === undefined) continue;
if (typeof block !== "string" || !text.includes(block)) throw new Error(`Hermes ${key.replace("_block", "")} block changed after enable; refusing destructive disable`);
text = text.replace(block, "");
}
return Buffer.from(text);
}
const rootBlock = operation.owned?.root_block;
const tablesBlock = operation.owned?.tables_block;
if (typeof rootBlock !== "string" || typeof tablesBlock !== "string") throw new Error("Codex integration journal lacks owned blocks");
let text = current.toString("utf8");
for (const [block, begin] of [[rootBlock, CODEX_NATIVE_ROOT_BEGIN], [tablesBlock, CODEX_NATIVE_TABLES_BEGIN]] as const) {
if (text.includes(begin) && !text.includes(block)) throw new Error("Codex Caveman config block changed after enable; refusing destructive disable");
text = text.replace(`${block}\n\n`, "").replace(`\n\n${block}\n`, "\n").replace(block, "");
}
return Buffer.from(text);View on GitHub (pinned to 3ee70a1026)