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

  1. 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.
  2. 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.
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18). Data as JSON: /api/errors/0035b271043949bd. Report an issue: GitHub.