{"record":{"id":"a3ddf29de09ee5ed","repo":"JuliusBrussee/caveman","slug":"file-already-exists-with-non-canonical-content","errorCode":null,"errorMessage":"${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill","messagePattern":"(.+?) already exists with non-canonical content; refusing to overwrite an unjournaled skill","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":2864,"sourceCode":"function ensureAgentNativeIntegration(agent: \"claude\" | \"codex\"): void {\n  const status = nativeIntegrationStatus(agent);\n  if (!status.installed) {\n    enableNative([agent]);\n    return;\n  }\n  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;","sourceCodeStart":2846,"sourceCodeEnd":2882,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L2846-L2882","documentation":"When installing agent-native skills with no existing journaled bundle, the CLI refuses to overwrite a skill file that already exists on disk with content differing from the canonical body. Without a journal proving Caveman created the file, overwriting could destroy a user's own file, so it throws instead.","triggerScenarios":"Running setup when no existing bundle journal is present and a target skill file already exists with non-canonical content (current bytes !== canonical body).","commonSituations":"User created a skill file at the same path themselves; a previous unjournaled/experimental install left files behind; upgrading from a version that did not journal skills.","solutions":["Move or delete the pre-existing skill file so the canonical version can be installed","Replace the file's content with the canonical body so the check passes","Install the bundle manually and let the journal take ownership of the files"],"exampleFix":"// before\nthrow new Error(`${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill`);\n// after\n// preserve the user's file, then install:\nrenameSync(file, `${file}.user-backup`);\nwriteFileSync(file, Buffer.from(body));","handlingStrategy":"validation","validationCode":"const target = join(skillDir, 'my-skill.md');\nif (existsSync(target)) {\n  const canonical = await fetchCanonicalBody();\n  if (!readFileSync(target).equals(Buffer.from(canonical))) {\n    console.error(`${target} exists with custom content; move it before setup.`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  runSetup(agent);\n} catch (e) {\n  if (String(e.message).includes('non-canonical content')) {\n    // rename the existing file to *.user-backup and re-run setup\n  }\n}","preventionTips":["Do not create your own files at Caveman's managed skill paths","Clean up remnants of experimental/unjournaled installs before setup","Upgrade via the CLI so journals are created from the start"],"tags":["skill-files","overwrite-protection","setup","cli"],"backgroundTag":"file-already-exists","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"}