{"record":{"id":"9b6ce1f86cd1ea06","repo":"JuliusBrussee/caveman","slug":"agent-caveman-cloud-mcp-changed-during-interrup-9b6ce1","errorCode":null,"errorMessage":"${agent} caveman-cloud MCP changed during interrupted removal; refusing destructive recovery","messagePattern":"(.+?) caveman-cloud MCP changed during interrupted removal; refusing destructive recovery","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":2506,"sourceCode":"    try { unlinkSync(agentNativeBundleJournalPath(agent)); } catch { /* removal already committed */ }\n    unlinkSync(agentNativeBundleRemovalJournalPath(agent));\n    process.stderr.write(`${mark(\"warn\")} completed interrupted ${agent} agent-native bundle removal\\n`);\n    return;\n  }\n  for (const skill of pending.skills) {\n    const current = fileBytes(skill.file);\n    if (current && bytesHash(current) !== skill.after_sha256 && !skillMatchesBefore(skill)) {\n      throw new Error(`${skill.file} changed during interrupted removal; refusing destructive recovery`);\n    }\n  }\n  const marker = readMcpServerMarker(agent, \"caveman-cloud\");\n  const markerKnown = !marker || sameMcpCommand(marker, pending.cloud_mcp) || sameMcpCommand(marker, pending.previous_cloud_mcp);\n  const hostIsInstalled = agentNativeCloudMcpMatches(agent, pending.cloud_mcp);\n  const hostIsPrevious = pending.previous_cloud_mcp\n    ? agentNativeCloudMcpMatches(agent, pending.previous_cloud_mcp)\n    : agentNativeCloudMcpHostAbsent(agent);\n  if (!markerKnown || (!hostIsInstalled && !hostIsPrevious)) {\n    throw new Error(`${agent} caveman-cloud MCP changed during interrupted removal; refusing destructive recovery`);\n  }\n  restoreInstalledAgentNativeBundle(agent, pending);\n  unlinkSync(agentNativeBundleRemovalJournalPath(agent));\n  process.stderr.write(`${mark(\"warn\")} rolled back interrupted ${agent} agent-native bundle removal before continuing\\n`);\n}\n\nfunction preflightAgentNativeSetup(agent: \"claude\" | \"codex\"): void {\n  recoverPendingAgentNativeRemoval(agent);\n  recoverPendingAgentNativeBundle(agent);\n  const profile = findAgent(agent)!;\n  if (!which(binOf(profile))) throw new Error(`${profile.display_name} not found on PATH`);\n  const mcpBinary = nativeMcpBinaryRequired();\n  const gw = gatewayURL();\n  nativeProxyBinaryRequired(gw);\n  withIntegrationLock(agent, () => recoverPendingNativeInstallUnlocked(agent));\n  const status = nativeIntegrationStatus(agent);\n  if (!status.installed) nativeMutationsFor(agent, gw, mcpBinary);\n}","sourceCodeStart":2488,"sourceCodeEnd":2524,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L2488-L2524","documentation":"The MCP guard of interrupted-removal recovery: the host caveman-cloud registration must match the journaled cloud_mcp or previous_cloud_mcp, and any marker file must match one of those. A registration that matches neither means it changed during the interrupted removal, so rolling back (restoring the installed bundle) would clobber the new state — recovery refuses instead.","triggerScenarios":"Interrupt an uninstall, then hand-edit or replace the caveman-cloud MCP entry (or delete it and add a different one), then run an agent command that triggers removal recovery.","commonSituations":"User reinstalled or reconfigured caveman-cloud manually; ran another caveman version's setup in the gap; deleted the marker file while changing the host registration.","solutions":["Revert your manual caveman-cloud registration changes so recovery can roll back to the journaled state","Or keep the new registration and clear the pending removal journal, then finish cleanup by hand (skill files, marker)","Use caveman's own install/uninstall verbs rather than editing MCP entries directly"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function removalMcpSafe(current: { command: string; args: string[] } | null, pending: { cloud_mcp: { command: string; args: string[] }; previous_cloud_mcp: { command: string; args: string[] } | null }): boolean {\n  const same = (a: { command: string; args: string[] } | null, b: { command: string; args: string[] } | null) =>\n    !!a && !!b && a.command === b.command && JSON.stringify(a.args) === JSON.stringify(b.args);\n  return same(current, pending.cloud_mcp) || same(current, pending.previous_cloud_mcp);\n}","typeGuard":"function isMcpChangedDuringRemovalError(e: unknown): boolean {\n  return e instanceof Error && e.message.includes(\"caveman-cloud MCP changed during interrupted removal\");\n}","tryCatchPattern":"try {\n  runCavemanAgentCommand(\"claude\");\n} catch (e) {\n  if (isMcpChangedDuringRemovalError(e)) {\n    // revert manual MCP edits or clear the pending removal journal, then re-run\n  } else throw e;\n}","preventionTips":["Finish or abort uninstalls before reconfiguring MCP servers","Manage caveman-cloud exclusively through caveman commands"],"tags":["recovery","mcp","uninstall","safety"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}