JuliusBrussee/caveman · error · Error

changed after setup; refusing to overwrite user skill edits

Error message

${file} changed after setup; refusing to overwrite user skill edits

What it means

When a journaled bundle already exists, the CLI allows overwriting a skill file only if it matches the canonical body or still matches the previous bundle's recorded after_sha256 (i.e. Caveman content). If the file differs from both, the user modified the installed skill, and re-installing would destroy those edits, so it throws.

Solutions

  1. Back up your edits, then restore the file to the previously installed content and re-run setup
  2. Copy your customizations into a separate user-level skill path that Caveman does not manage
  3. Accept losing edits by deleting the old journal entry's claim (re-initialize the bundle) — only if edits are disposable

Example fix

// before
throw new Error(`${file} changed after setup; refusing to overwrite user skill edits`);
// after
// keep the user's version and skip reinstalling that skill:
copyFileSync(file, `${file}.user`); // preserve, then decide manually
Defensive patterns

Strategy: validation

Validate before calling

const prev = existingBundle.skills.find(s => s.file === file);
const cur = readFileSync(file);
const userEdited = !cur.equals(Buffer.from(body)) &&
  (!prev || createHash('sha256').update(cur).digest('hex') !== prev.after_sha256);
if (userEdited) console.error(`${file} has local edits; back them up before re-running setup.`);

Try / catch

try {
  runSetup(agent);
} catch (e) {
  if (String(e.message).includes('refusing to overwrite user skill edits')) {
    // copy your edited file aside, restore or accept the canonical version, retry
  }
}

Prevention

When it happens

Trigger: Re-running setup with an existing bundle journal where a skill file's current bytes differ from the new canonical body AND from the previous journal entry's after_sha256 hash.

Common situations: User customized a Caveman-managed skill file (tweaked prompts) and later re-runs setup/update; a merge or code-review tool modified installed skills.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/f525adfcb2ea5ef4. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:2870

  if (status.state !== "installed") repairNativeAgent(agent);
}

function preflightAgentNativeBundleComponents(
  agent: "claude" | "codex",
  existingBundle: AgentNativeBundleJournal | null,
): void {
  const previousSkills = new Map(existingBundle?.skills.map((skill) => [skill.file, skill]) ?? []);
  for (const { file, body } of agentNativeSkillFiles(agent)) {
    const current = fileBytes(file);
    if (!existingBundle) {
      if (current && !current.equals(Buffer.from(body))) {
        throw new Error(`${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill`);
      }
      continue;
    }
    const previous = previousSkills.get(file);
    if (current && !current.equals(Buffer.from(body)) && (!previous || bytesHash(current) !== previous.after_sha256)) {
      throw new Error(`${file} changed after setup; refusing to overwrite user skill edits`);
    }
  }
  if (agent === "claude") {
    const actual = claudeMcpRegistration("caveman-cloud");
    const marker = readMcpServerMarker("claude", "caveman-cloud");
    if (actual.present && !marker) {
      throw new Error("Claude caveman-cloud MCP exists but is not Caveman-journaled; refusing overwrite");
    }
    if (actual.present && marker && !agentNativeCloudMcpMatches("claude", marker)) {
      throw new Error("Claude caveman-cloud MCP changed after setup; refusing to overwrite user fields");
    }
    return;
  }
  const config = fileBytes(join(codexHomeDir(), "config.toml"))?.toString("utf8") ?? "";
  if (!config.includes("[mcp_servers.caveman-cloud]")) return;
  if (!readMcpServerMarker("codex", "caveman-cloud")) {
    throw new Error("[mcp_servers.caveman-cloud] exists but is not Caveman-journaled; refusing overwrite");
  }

View on GitHub (pinned to 3ee70a1026)