{"record":{"id":"0035b271043949bd","repo":"JuliusBrussee/caveman","slug":"integration-backup-hash-mismatch-operation-back","errorCode":null,"errorMessage":"integration backup hash mismatch: ${operation.backup}","messagePattern":"integration backup hash mismatch: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7995,"sourceCode":"      continue;\n    }\n    process.stderr.write(`${mark(\"ok\")} ${profile.display_name}: ${agent === \"aider\" ? \"shallow\" : \"native\"} Caveman enabled; run ${agent} normally\\n`);\n    if (agent !== \"aider\") process.stderr.write(dim(`→ host trust remains authoritative; approve Caveman hooks/plugin when ${profile.display_name} asks\\n`));\n    if (agent === \"codex\") process.stderr.write(dim(\"→ review/approve hook hashes through Codex /hooks; Caveman does not bypass native trust\\n\"));\n    if (agent === \"aider\") {\n      process.stderr.write(dim(`→ coding policy: Core ${NATIVE_PACK.version} static on; Aider cannot apply think.core live; \\`caveman disable aider\\` removes it\\n`));\n    } else {\n      const core = wrapRuntimeConfig().core;\n      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`));\n    }\n    process.stderr.write(dim(`→ undo: caveman disable ${agent}\\n`));\n  }\n}\n\nfunction nativeBackupBytes(operation: NativeJournal[\"operations\"][number]): Buffer | null {\n  if (!operation.before_exists) return null;\n  const bytes = readFileSync(operation.backup);\n  if (operation.before_sha256 && bytesHash(bytes) !== operation.before_sha256) throw new Error(`integration backup hash mismatch: ${operation.backup}`);\n  return bytes;\n}\n\nfunction removeNativeHookEntries(root: Record<string, unknown>, agent: \"claude\" | \"codex\" | \"gemini\"): Record<string, unknown> {\n  const hooks = root.hooks && typeof root.hooks === \"object\" && !Array.isArray(root.hooks)\n    ? root.hooks as Record<string, unknown>\n    : undefined;\n  if (!hooks) return root;\n  const expected = nativeHooksDocument(agent, true).hooks as Record<string, unknown>;\n  const allowedManaged = new Set(\n    Object.values(expected)\n      .flatMap((raw) => Array.isArray(raw) ? raw as Array<Record<string, unknown>> : [])\n      .map(canonicalManagedHookEntry)\n      .filter(Boolean),\n  );\n  for (const event of Object.keys(hooks)) {\n    const expectedRaw = expected[event];\n    const list = Array.isArray(hooks[event]) ? hooks[event] as Array<Record<string, unknown>> : [];","sourceCodeStart":7977,"sourceCodeEnd":8013,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L7977-L8013","documentation":"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.","triggerScenarios":"`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.","commonSituations":"Cloud-sync or backup agents rewriting ~/.caveman; users pruning 'cache-like' directories; partial rsync of the home dir between machines.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await runCaveman('disable', agent);\n} catch (e) {\n  if (/integration backup hash mismatch/.test(String(e))) {\n    // backup .bin is corrupt: restore configs from your own copy, or remove caveman blocks by hand and delete ~/.caveman/integrations/<agent>.json\n    throw new Error(`backup corrupted for ${agent}; manual restore required`);\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["backup","integrity","sha256","disable"],"backgroundTag":"backup-integrity-mismatch","analyzedSha":"5184b3d11ac6a1acb7d44b9bfaa31698157cff97","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}