JuliusBrussee/caveman · error · Error
${skill.file} changed during interrupted removal; refusing d
Error message
${skill.file} changed during interrupted removal; refusing destructive recovery What it means
During recovery of an interrupted removal, each journaled skill file is checked: if it still exists, its hash must equal the recorded after_sha256, or the file must byte-equal the pre-install original (skillMatchesBefore). A file that exists but matches neither was edited by the user after the interrupted removal, and deleting it (completing the removal) would destroy those edits — so recovery refuses.
Source
Thrown at packages/cli/src/index.ts:2496
installAgentNativeCloudMcp(agent, journal.cloud_mcp);
installAgentNativeBundleSkills(agent, journal);
verifyAgentNativeBundle(agent, journal, journal.cloud_mcp);
atomicWriteFile(agentNativeBundleJournalPath(agent), Buffer.from(JSON.stringify(journal, null, 2) + "\n"));
}
function recoverPendingAgentNativeRemoval(agent: "claude" | "codex"): void {
const pending = readPendingAgentNativeBundleRemoval(agent);
if (!pending) return;
if (agentNativeBundleRemovalCompleted(agent, pending)) {
try { unlinkSync(agentNativeBundleJournalPath(agent)); } catch { /* removal already committed */ }
unlinkSync(agentNativeBundleRemovalJournalPath(agent));
process.stderr.write(`${mark("warn")} completed interrupted ${agent} agent-native bundle removal\n`);
return;
}
for (const skill of pending.skills) {
const current = fileBytes(skill.file);
if (current && bytesHash(current) !== skill.after_sha256 && !skillMatchesBefore(skill)) {
throw new Error(`${skill.file} changed during interrupted removal; refusing destructive recovery`);
}
}
const marker = readMcpServerMarker(agent, "caveman-cloud");
const markerKnown = !marker || sameMcpCommand(marker, pending.cloud_mcp) || sameMcpCommand(marker, pending.previous_cloud_mcp);
const hostIsInstalled = agentNativeCloudMcpMatches(agent, pending.cloud_mcp);
const hostIsPrevious = pending.previous_cloud_mcp
? agentNativeCloudMcpMatches(agent, pending.previous_cloud_mcp)
: agentNativeCloudMcpHostAbsent(agent);
if (!markerKnown || (!hostIsInstalled && !hostIsPrevious)) {
throw new Error(`${agent} caveman-cloud MCP changed during interrupted removal; refusing destructive recovery`);
}
restoreInstalledAgentNativeBundle(agent, pending);
unlinkSync(agentNativeBundleRemovalJournalPath(agent));
process.stderr.write(`${mark("warn")} rolled back interrupted ${agent} agent-native bundle removal before continuing\n`);
}
function preflightAgentNativeSetup(agent: "claude" | "codex"): void {
recoverPendingAgentNativeRemoval(agent);View on GitHub (pinned to 27d5a3981a)
Solutions
- Back up your edited skill file, delete it (or restore the original/after content), and re-run so removal completes
- Or keep your edits and remove the pending removal journal plus the skill files manually, accepting you own the cleanup
Defensive patterns
Strategy: validation
Validate before calling
function skillSafeToRemove(file: string, afterSha: string, beforeB64: string | null): boolean {
let bytes: Buffer;
try { bytes = readFileSync(file); } catch { return true; } // gone = fine
const h = createHash("sha256").update(bytes).digest("hex");
if (h === afterSha) return true;
if (beforeB64 === null) return false;
return bytes.equals(Buffer.from(beforeB64, "base64"));
} Type guard
function isSkillChangedDuringRemovalError(e: unknown): boolean {
return e instanceof Error && e.message.endsWith("changed during interrupted removal; refusing destructive recovery");
} Try / catch
try {
runCavemanAgentCommand("codex");
} catch (e) {
if (isSkillChangedDuringRemovalError(e)) {
// message names the file — back it up, move it aside, re-run to complete removal
} else throw e;
} Prevention
- Do not edit skill files between an interrupted uninstall and its recovery
- Keep personal skill content in separate directories from the caveman suite
When it happens
Trigger: Kill an uninstall mid-way, edit one of the still-present Caveman skill files, then run any command that triggers recoverPendingAgentNativeRemoval.
Common situations: User customized a skill thinking uninstall was complete; an agent tool rewrote SKILL.md bodies; pixel convert ran over the skills directory in between.
Related errors
- ${skill.file} changed during interrupted setup; refusing des
- ${agent} caveman-cloud MCP changed during interrupted remova
- cannot read pending ${agent} agent-native removal journal: $
- ${agent} caveman-cloud MCP changed during interrupted setup;
- ${file} already exists with non-canonical content; refusing
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/21408c8e09171ac4.
Report an issue: GitHub.