JuliusBrussee/caveman · error
existing Hermes Caveman native block is unjournaled; run…
Error message
existing Hermes Caveman native block is unjournaled; run `caveman doctor hermes`
What it means
hermesNativeConfig() refuses to edit the Hermes config if it already contains any Caveman native block markers (route, plugin, or MCP begin/end). Presence of these markers means a prior install was not journaled, so the installer cannot safely transform the config and tells the user to run `caveman doctor hermes`.
Solutions
- Run `caveman doctor hermes` to strip/reconcile the stale blocks, then re-run enable.
- Manually delete the Caveman marker-delimited blocks (route, plugin, MCP) from the Hermes config, then re-run install.
- Restore the Hermes config to its pre-Caveman state from backup and re-run the enable flow.
Example fix
// before (hermes config.yaml) model: # caveman:native-route-begin provider: "custom" # caveman:native-route-end // after `caveman doctor hermes` model: provider: "anthropic"
Defensive patterns
Strategy: validation
Validate before calling
const cfg = readFileSync(hermesCfg, 'utf8');
const markers = [ROUTE_BEGIN, ROUTE_END, PLUGIN_BEGIN, PLUGIN_END, MCP_BEGIN, MCP_END];
if (markers.some((m) => cfg.includes(m))) {
// run `caveman doctor hermes` before enabling
} Try / catch
try {
await cavemanEnableHermes();
} catch (e) {
if (String(e.message).includes('unjournaled')) {
await cavemanDoctorHermes();
await cavemanEnableHermes();
}
} Prevention
- Use the Caveman CLI (not manual config edits) to enable/disable Hermes integration.
- Run `caveman doctor hermes` after restoring the Hermes config from backup.
- Do not manually delete journal state while config blocks remain.
- Avoid copying configured Hermes configs between machines without running doctor.
When it happens
Trigger: Running the Hermes enable/install flow when the Hermes YAML config already includes HERMES_NATIVE_ROUTE/PLUGIN/MCP BEGIN or END marker lines from an earlier, unjournaled install.
Common situations: An install that crashed between writing the config and updating the journal; restoring the Hermes config from a backup that contains blocks; copying a configured config.yaml from another machine; journal files deleted manually.
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
- existing Aider Caveman block is unjournaled; run `caveman…
- : MCP ownership journal failed; native config may already…
- exists and is not exact Caveman-owned content; refusing to…
- could not install caveman-cloud MCP for
- existing Codex Caveman block is corrupted; run `caveman…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/4ba193ca3c54f70d.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:7730
end: string,
blockLines: string[],
): string | null {
const section = topLevelSection(lines, sectionName);
if (section) {
if (sectionHasChildKey(lines, section, childName)) return null;
lines.splice(section.start + 1, 0, ...blockLines);
return blockLines.join("\n");
}
if (lines.length > 0 && lines[lines.length - 1]!.trim()) lines.push("");
const body = blockLines.filter((line) => !line.includes(begin) && !line.includes(end));
const whole = [begin, `${sectionName}:`, ...body, end];
lines.push(...whole);
return whole.join("\n");
}
function hermesNativeConfig(source: string, gw: string, mcpBinary: string): { text: string; owned: Record<string, unknown> } {
for (const marker of [HERMES_NATIVE_ROUTE_BEGIN, HERMES_NATIVE_ROUTE_END, HERMES_NATIVE_PLUGIN_BEGIN, HERMES_NATIVE_PLUGIN_END, HERMES_NATIVE_MCP_BEGIN, HERMES_NATIVE_MCP_END]) {
if (source.includes(marker)) throw new Error("existing Hermes Caveman native block is unjournaled; run `caveman doctor hermes`");
}
const lines = yamlLines(source);
let model = topLevelSection(lines, "model");
if (!model) {
lines.unshift("model:");
model = topLevelSection(lines, "model")!;
}
const routeIndexes: number[] = [];
const previousRouteLines: string[] = [];
for (let i = model.start + 1; i < model.end; i++) {
if (/^ (provider|base_url):/.test(lines[i]!)) {
routeIndexes.push(i);
previousRouteLines.push(lines[i]!);
}
}
if (new Set(routeIndexes.map((index) => lines[index]!.match(/^ ([^:]+):/)?.[1])).size !== routeIndexes.length) {
throw new Error("Hermes model provider/base_url keys are duplicated; refusing ambiguous install");
}View on GitHub (pinned to 3ee70a1026)