{"record":{"id":"f525adfcb2ea5ef4","repo":"JuliusBrussee/caveman","slug":"file-changed-after-setup-refusing-to-overwrite","errorCode":null,"errorMessage":"${file} changed after setup; refusing to overwrite user skill edits","messagePattern":"(.+?) changed after setup; refusing to overwrite user skill edits","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":2870,"sourceCode":"  if (status.state !== \"installed\") repairNativeAgent(agent);\n}\n\nfunction preflightAgentNativeBundleComponents(\n  agent: \"claude\" | \"codex\",\n  existingBundle: AgentNativeBundleJournal | null,\n): void {\n  const previousSkills = new Map(existingBundle?.skills.map((skill) => [skill.file, skill]) ?? []);\n  for (const { file, body } of agentNativeSkillFiles(agent)) {\n    const current = fileBytes(file);\n    if (!existingBundle) {\n      if (current && !current.equals(Buffer.from(body))) {\n        throw new Error(`${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill`);\n      }\n      continue;\n    }\n    const previous = previousSkills.get(file);\n    if (current && !current.equals(Buffer.from(body)) && (!previous || bytesHash(current) !== previous.after_sha256)) {\n      throw new Error(`${file} changed after setup; refusing to overwrite user skill edits`);\n    }\n  }\n  if (agent === \"claude\") {\n    const actual = claudeMcpRegistration(\"caveman-cloud\");\n    const marker = readMcpServerMarker(\"claude\", \"caveman-cloud\");\n    if (actual.present && !marker) {\n      throw new Error(\"Claude caveman-cloud MCP exists but is not Caveman-journaled; refusing overwrite\");\n    }\n    if (actual.present && marker && !agentNativeCloudMcpMatches(\"claude\", marker)) {\n      throw new Error(\"Claude caveman-cloud MCP changed after setup; refusing to overwrite user fields\");\n    }\n    return;\n  }\n  const config = fileBytes(join(codexHomeDir(), \"config.toml\"))?.toString(\"utf8\") ?? \"\";\n  if (!config.includes(\"[mcp_servers.caveman-cloud]\")) return;\n  if (!readMcpServerMarker(\"codex\", \"caveman-cloud\")) {\n    throw new Error(\"[mcp_servers.caveman-cloud] exists but is not Caveman-journaled; refusing overwrite\");\n  }","sourceCodeStart":2852,"sourceCodeEnd":2888,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L2852-L2888","documentation":"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.","triggerScenarios":"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.","commonSituations":"User customized a Caveman-managed skill file (tweaked prompts) and later re-runs setup/update; a merge or code-review tool modified installed skills.","solutions":["Back up your edits, then restore the file to the previously installed content and re-run setup","Copy your customizations into a separate user-level skill path that Caveman does not manage","Accept losing edits by deleting the old journal entry's claim (re-initialize the bundle) — only if edits are disposable"],"exampleFix":"// before\nthrow new Error(`${file} changed after setup; refusing to overwrite user skill edits`);\n// after\n// keep the user's version and skip reinstalling that skill:\ncopyFileSync(file, `${file}.user`); // preserve, then decide manually","handlingStrategy":"validation","validationCode":"const prev = existingBundle.skills.find(s => s.file === file);\nconst cur = readFileSync(file);\nconst userEdited = !cur.equals(Buffer.from(body)) &&\n  (!prev || createHash('sha256').update(cur).digest('hex') !== prev.after_sha256);\nif (userEdited) console.error(`${file} has local edits; back them up before re-running setup.`);","typeGuard":null,"tryCatchPattern":"try {\n  runSetup(agent);\n} catch (e) {\n  if (String(e.message).includes('refusing to overwrite user skill edits')) {\n    // copy your edited file aside, restore or accept the canonical version, retry\n  }\n}","preventionTips":["Keep customizations in a separate user skill location, not in managed files","Diff managed files against canonical before re-running setup","Use git to track local edits so they can be reapplied after updates"],"tags":["skill-files","overwrite-protection","user-edits","cli"],"backgroundTag":"checksum-mismatch","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}