JuliusBrussee/caveman · error · Error
Hermes integration journal lacks owned routing block
Error message
Hermes integration journal lacks owned routing block
What it means
The Hermes integration journal (~/.caveman/integrations/hermes.json) exists but its owned metadata is malformed for the hermes-config operation: route_block is not a string or previous_route_lines is not an array. Those fields hold the exact routing block Caveman inserted and the lines it replaced; without them the disable cannot restore the original config, so it aborts. This indicates a journal written by an incompatible Caveman version or one that was truncated/edited.
Source
Thrown at packages/cli/src/index.ts:7713
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");
}
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, "");View on GitHub (pinned to 2f49f0e1a3)
Solutions
- Re-run `caveman enable hermes` to write a fresh journal against the live config, then `caveman disable hermes`
- Upgrade to the Caveman version that performed the enable and disable with it
- Manual path: delete the caveman routing/plugin/mcp blocks from the Hermes config, then remove ~/.caveman/integrations/hermes.json and .pending-hermes.json
Example fix
# before: malformed journal caveman disable hermes # >> Hermes integration journal lacks owned routing block # after: rewrite journal, then disable caveman enable hermes && caveman disable hermes
Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'node:fs';
const home = process.env.CAVEMAN_HOME ?? `${process.env.HOME}/.caveman`;
const j = JSON.parse(readFileSync(`${home}/integrations/hermes.json`, 'utf8'));
const valid = j.operations.every((op) =>
op.kind !== 'hermes-config' ||
(typeof op.owned?.route_block === 'string' && Array.isArray(op.owned?.previous_route_lines)));
if (!valid) console.error('run `caveman enable hermes` to rewrite the journal before disable'); Type guard
const hasHermesOwnedRouting = (op) => typeof op.owned?.route_block === 'string' && Array.isArray(op.owned?.previous_route_lines);
Try / catch
catch (err) { if (err.message.includes('journal lacks owned routing block')) { /* re-enable to rewrite journal, then disable */ } else throw err; } Prevention
- Do not edit or partially copy ~/.caveman/integrations/hermes.json
- Use matching caveman versions for enable and disable
When it happens
Trigger: `caveman disable hermes` / `caveman doctor hermes --fix` with a journal whose operations[].owned lacks a string route_block or an array previous_route_lines — after a version downgrade, partial write, or manual edit of the journal file.
Common situations: Rolling Caveman back to a version with a different journal schema; a crashed enable leaving a half-written journal; hand-editing the integrations directory.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Gemini integration journal lacks owned routing block
- Aider integration journal lacks owned blocks
- Codex integration journal lacks owned blocks
- Hermes routing block changed after enable; refusing destruct
- Hermes ${key.replace("_block", "")} block changed after enab
AI-assisted analysis of JuliusBrussee/caveman@2f49f0e1a3 (2026-08-18).
Data as JSON: /api/errors/c2dcaeb2f7d028e6.
Report an issue: GitHub.