JuliusBrussee/caveman · error · Error
already exists with non-canonical content; refusing to…
Error message
${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill What it means
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.
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
Example fix
// before
throw new Error(`${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill`);
// after
// preserve the user's file, then install:
renameSync(file, `${file}.user-backup`);
writeFileSync(file, Buffer.from(body)); Defensive patterns
Strategy: validation
Validate before calling
const target = join(skillDir, 'my-skill.md');
if (existsSync(target)) {
const canonical = await fetchCanonicalBody();
if (!readFileSync(target).equals(Buffer.from(canonical))) {
console.error(`${target} exists with custom content; move it before setup.`);
}
} Try / catch
try {
runSetup(agent);
} catch (e) {
if (String(e.message).includes('non-canonical content')) {
// rename the existing file to *.user-backup and re-run setup
}
} Prevention
- 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
When it happens
Trigger: Running setup when no existing bundle journal is present and a target skill file already exists with non-canonical content (current bytes !== canonical body).
Common situations: 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.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- changed after setup; refusing to overwrite user skill edits
- native integration postflight is
- agent-native setup failed
- caveman-mcp lacks current mcp_recovery capability; run…
- caveman-proxy not found; run `caveman setup --install`
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/a3ddf29de09ee5ed.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:2864
function ensureAgentNativeIntegration(agent: "claude" | "codex"): void {
const status = nativeIntegrationStatus(agent);
if (!status.installed) {
enableNative([agent]);
return;
}
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;View on GitHub (pinned to 3ee70a1026)