JuliusBrussee/caveman · error
${agent} caveman-cloud MCP changed after setup; refusing des
Error message
${agent} caveman-cloud MCP changed after setup; refusing destructive bundle removal What it means
Safety gate in removeAgentNativeBundle: the live caveman-cloud MCP marker must still equal journal.cloud_mcp (sameMcpCommand) and the agent registration must still match (agentNativeCloudMcpMatches). If either drifted since setup, removal refuses to proceed so it does not clobber a registration that may contain newer user intent.
Source
Thrown at packages/cli/src/index.ts:2614
if (!current || bytesHash(current) !== skill.after_sha256) throw new Error(`${skill.file} failed skill postflight`);
}
}
function removeAgentNativeBundle(agent: "claude" | "codex"): void {
recoverPendingAgentNativeRemoval(agent);
recoverPendingAgentNativeBundle(agent);
const journal = readAgentNativeBundleJournal(agent);
if (!journal) {
process.stderr.write(`${mark("warn")} ${agent}: no agent-native bundle journal found\n`);
return;
}
for (const skill of journal.skills) {
const current = fileBytes(skill.file);
if (!current || bytesHash(current) !== skill.after_sha256) throw new Error(`${skill.file} changed after setup; refusing destructive bundle removal`);
}
const marker = readMcpServerMarker(agent, "caveman-cloud");
if (!sameMcpCommand(marker, journal.cloud_mcp) || !agentNativeCloudMcpMatches(agent, journal.cloud_mcp)) {
throw new Error(`${agent} caveman-cloud MCP changed after setup; refusing destructive bundle removal`);
}
if (journal.native_owned) {
const nativeJournal = readNativeJournal(agent);
if (nativeJournal) {
for (const operation of nativeJournal.operations) restoreNativeOperation(operation);
}
}
atomicWriteFile(agentNativeBundleRemovalJournalPath(agent), Buffer.from(JSON.stringify(journal, null, 2) + "\n"));
try {
restoreAgentNativeBundleSkills(journal.skills);
restoreAgentNativeCloudMcp(agent, journal.previous_cloud_mcp);
if (journal.native_owned) disableNativeAgent(agent);
unlinkSync(agentNativeBundleJournalPath(agent));
unlinkSync(agentNativeBundleRemovalJournalPath(agent));
} catch (error) {
try {
restoreInstalledAgentNativeBundle(agent, journal);
unlinkSync(agentNativeBundleRemovalJournalPath(agent));View on GitHub (pinned to 27d5a3981a)
Solutions
- Re-run `caveman setup --agent-native <agent>` to re-register the MCP entry exactly as the journal expects, then run --remove
- Or manually restore the caveman-cloud entry in the agent config to the journaled command/args, then remove
- If the drift is intentional, delete the agent-native bundle journal only after manually confirming no skill/MCP state you care about will be orphaned
Defensive patterns
Strategy: validation
Validate before calling
// before --remove: confirm live MCP equals the journal entry
const marker = readMcpServerMarker(agent, "caveman-cloud");
if (!sameMcpCommand(marker, journal.cloud_mcp)) {
throw new Error("caveman-cloud entry drifted from journal; re-run setup before --remove");
} Try / catch
try {
await setup(["--agent-native", agent, "--remove"]);
} catch (error) {
if (/caveman-cloud MCP changed after setup/.test((error as Error).message)) {
await setup(["--agent-native", agent]); // re-register exactly as journaled
await setup(["--agent-native", agent, "--remove"]);
} else throw error;
} Prevention
- Do not repoint the caveman-cloud MCP entry while a bundle is installed; remove the bundle first
- Avoid running two caveman versions against the same agent config
- Before --remove, diff the live MCP entry against what setup originally wrote
When it happens
Trigger: Running `--remove` after the user or another tool changed the caveman-cloud MCP entry (different command, args, or resolved registration) relative to what the bundle journal recorded.
Common situations: User pointed caveman-cloud at a different binary or gateway; a second caveman version re-registered the MCP entry; agent re-serialized args.
Related errors
- ${agent} caveman-cloud MCP postflight mismatch
- ${skill.file} changed after setup; refusing destructive bund
- ${key} is required
- filters must be an object
- unknown trace filter(s): ${unknown.sort().join(", ")}
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/9ea4397e141c7e33.
Report an issue: GitHub.