JuliusBrussee/caveman · error · Error
integration backup hash mismatch: ${operation.backup}
Error message
integration backup hash mismatch: ${operation.backup} What it means
Every journaled operation stores its pre-enable bytes in ~/.caveman/integrations/backups/<agent>/<uuid>/<i>.bin with a sha256 of the before content (before_sha256). nativeBackupBytes() re-reads a backup during disable or recovery and throws when the stored hash no longer matches - the restore source itself is corrupt, so nothing is restored from it.
Source
Thrown at packages/cli/src/index.ts:7995
continue;
}
process.stderr.write(`${mark("ok")} ${profile.display_name}: ${agent === "aider" ? "shallow" : "native"} Caveman enabled; run ${agent} normally\n`);
if (agent !== "aider") process.stderr.write(dim(`→ host trust remains authoritative; approve Caveman hooks/plugin when ${profile.display_name} asks\n`));
if (agent === "codex") process.stderr.write(dim("→ review/approve hook hashes through Codex /hooks; Caveman does not bypass native trust\n"));
if (agent === "aider") {
process.stderr.write(dim(`→ coding policy: Core ${NATIVE_PACK.version} static on; Aider cannot apply think.core live; \`caveman disable aider\` removes it\n`));
} else {
const core = wrapRuntimeConfig().core;
process.stderr.write(dim(`→ coding policy: Core ${NATIVE_PACK.version} ${core ? "on" : "off"}; change with \`caveman tools config set think.core ${core ? "off" : "on"}\`; start new session to clear previously delivered context\n`));
}
process.stderr.write(dim(`→ undo: caveman disable ${agent}\n`));
}
}
function nativeBackupBytes(operation: NativeJournal["operations"][number]): Buffer | null {
if (!operation.before_exists) return null;
const bytes = readFileSync(operation.backup);
if (operation.before_sha256 && bytesHash(bytes) !== operation.before_sha256) throw new Error(`integration backup hash mismatch: ${operation.backup}`);
return bytes;
}
function removeNativeHookEntries(root: Record<string, unknown>, agent: "claude" | "codex" | "gemini"): Record<string, unknown> {
const hooks = root.hooks && typeof root.hooks === "object" && !Array.isArray(root.hooks)
? root.hooks as Record<string, unknown>
: undefined;
if (!hooks) return root;
const expected = nativeHooksDocument(agent, true).hooks as Record<string, unknown>;
const allowedManaged = new Set(
Object.values(expected)
.flatMap((raw) => Array.isArray(raw) ? raw as Array<Record<string, unknown>> : [])
.map(canonicalManagedHookEntry)
.filter(Boolean),
);
for (const event of Object.keys(hooks)) {
const expectedRaw = expected[event];
const list = Array.isArray(hooks[event]) ? hooks[event] as Array<Record<string, unknown>> : [];View on GitHub (pinned to 5184b3d11a)
Solutions
- If you keep your own pre-enable copy of the config, restore it manually and then remove ~/.caveman/integrations/<agent>.json so disable turns into a no-op.
- Otherwise reconcile by hand: run `caveman doctor <agent>` to list the managed files, remove the Caveman-owned blocks from each, then delete ~/.caveman/integrations/<agent>.json and the backups dir for that agent.
- Treat recurrence as filesystem/sync corruption - exclude ~/.caveman from sync tools.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await runCaveman('disable', agent);
} catch (e) {
if (/integration backup hash mismatch/.test(String(e))) {
// backup .bin is corrupt: restore configs from your own copy, or remove caveman blocks by hand and delete ~/.caveman/integrations/<agent>.json
throw new Error(`backup corrupted for ${agent}; manual restore required`);
}
throw e;
} Prevention
- Keep your own pre-enable copies of agent configs (dotfiles) so restore never depends solely on caveman backups
- Exclude ~/.caveman from sync/backup tools that rewrite files in place
- Never prune ~/.caveman/integrations/backups selectively
When it happens
Trigger: `caveman disable <agent>` or crash recovery after a backup .bin file was modified, truncated, or replaced: disk-full during the original write, sync/backup tools touching ~/.caveman, manual cleanup of the backups directory, or a bad copy between machines.
Common situations: Cloud-sync or backup agents rewriting ~/.caveman; users pruning 'cache-like' directories; partial rsync of the home dir between machines.
Related errors
- cave_harness_plan_digest_mismatch
- cave_harness_artifact_digest_invalid
- cave_harness_incomplete_evidence
- cave_runtime_segment_id_collision
- cave_execution_plan_selection_mismatch
AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18).
Data as JSON: /api/errors/0035b271043949bd.
Report an issue: GitHub.