JuliusBrussee/caveman · error
Hermes integration journal lacks owned routing block
Error message
Hermes integration journal lacks owned routing block
What it means
Hermes config rollback needs the journaled route_block string and the previous_route_lines array (the pre-enable content to restore). If either is missing or of the wrong type in operation.owned, the rollback cannot proceed and the tool throws. This indicates corrupt/incomplete journal state rather than a user config problem.
Solutions
- Manually remove the Caveman routing block from the Hermes config and restore prior routing lines by hand.
- Re-run enable with the current CLI version to regenerate a complete journal, then disable.
- Upgrade/downgrade the CLI so both operations share the same journal schema.
Example fix
// corrupt journal
{ "owned": { "route_block": 42 } }
// after re-enable
{ "owned": { "route_block": "# caveman-route ...", "previous_route_lines": ["model: x"] } } Defensive patterns
Strategy: validation
Validate before calling
const owned = journal.owned ?? {};
if (typeof owned.route_block !== 'string' || !Array.isArray(owned.previous_route_lines)) {
throw new Error('Hermes journal incomplete; re-enable before disabling');
} Type guard
const hasHermesRouteJournal = (owned) => typeof owned?.route_block === 'string' && owned.route_block.length > 0 && Array.isArray(owned?.previous_route_lines);
Try / catch
try {
await caveman.disable(['hermes']);
} catch (err) {
if (err.message.includes('lacks owned routing block')) {
console.error('Journal corrupt; strip the Caveman routing block from the Hermes config manually.');
} else throw err;
} Prevention
- Don't edit the Caveman state/journal files.
- Run enable and disable with the same CLI version.
- Re-enable to regenerate the journal if you suspect schema drift.
- Back up the Hermes config before enable so manual restore is possible.
When it happens
Trigger: Running disable with operation.kind === "hermes-config" when operation.owned.route_block is not a string or operation.owned.previous_route_lines is not an array — journal written by an older CLI version, truncated, or hand-edited.
Common situations: CLI version skew between enable (old journal schema) and disable (new schema); state file corrupted; partially synced dotfile state across machines.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Aider integration journal lacks owned blocks
- Codex integration journal lacks owned blocks
- Hermes block changed after enable; refusing destructive…
- Hermes routing block changed after enable; refusing…
- : MCP ownership journal failed; native config may already…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/c2dcaeb2f7d028e6.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:8408
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 3ee70a1026)