{"record":{"id":"fcb6810b62d1891e","repo":"JuliusBrussee/caveman","slug":"agent-caveman-cloud-mcp-changed-during-interrup","errorCode":null,"errorMessage":"${agent} caveman-cloud MCP changed during interrupted setup; refusing destructive recovery","messagePattern":"(.+?) caveman-cloud MCP changed during interrupted setup; refusing destructive recovery","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":2443,"sourceCode":"    const currentMatchesBefore = current === null ? before === null : before !== null && bytesHash(current) === bytesHash(before);\n    const currentHash = current ? bytesHash(current) : null;\n    const currentMatchesCommitted = currentHash !== null && currentHash === committedSkills.get(skill.file);\n    if (!currentMatchesBefore && !currentMatchesCommitted && currentHash !== skill.after_sha256) {\n      throw new Error(`${skill.file} changed during interrupted setup; refusing destructive recovery`);\n    }\n  }\n  const marker = readMcpServerMarker(agent, \"caveman-cloud\");\n  const markerKnown = !marker\n    || sameMcpCommand(marker, pending.cloud_mcp)\n    || sameMcpCommand(marker, pending.previous_cloud_mcp)\n    || sameMcpCommand(marker, committed?.cloud_mcp ?? null);\n  const hostInstalled = agentNativeCloudMcpMatches(agent, pending.cloud_mcp);\n  const hostPrevious = pending.previous_cloud_mcp\n    ? agentNativeCloudMcpMatches(agent, pending.previous_cloud_mcp)\n    : agentNativeCloudMcpHostAbsent(agent);\n  const hostCommitted = committed ? agentNativeCloudMcpMatches(agent, committed.cloud_mcp) : false;\n  if (!markerKnown || (!hostInstalled && !hostPrevious && !hostCommitted)) {\n    throw new Error(`${agent} caveman-cloud MCP changed during interrupted setup; refusing destructive recovery`);\n  }\n  restoreInstalledAgentNativeBundle(agent, pending);\n  unlinkSync(agentNativeBundleJournalPath(agent, true));\n  process.stderr.write(`${mark(\"warn\")} completed interrupted ${agent} agent-native bundle before continuing\\n`);\n}\n\nfunction skillMatchesBefore(skill: AgentNativeBundleSkill): boolean {\n  const current = fileBytes(skill.file);\n  const before = skill.before_base64 === null ? null : Buffer.from(skill.before_base64, \"base64\");\n  return current === null ? before === null : before !== null && current.equals(before);\n}\n\nfunction agentNativeCloudMcpHostAbsent(agent: \"claude\" | \"codex\"): boolean {\n  if (agent === \"claude\") return !claudeMcpRegistration(\"caveman-cloud\").present;\n  const config = fileBytes(join(homedir(), \".codex\", \"config.toml\"))?.toString(\"utf8\") ?? \"\";\n  return !config.includes(\"[mcp_servers.caveman-cloud]\");\n}\n","sourceCodeStart":2425,"sourceCodeEnd":2461,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L2425-L2461","documentation":"The MCP-side guard of interrupted-setup recovery: the on-disk caveman-cloud registration must match the pending target, the recorded previous MCP, or the committed journal's MCP; the marker file (if present) must equally match one of those. If neither holds, the user (or another tool) changed the MCP registration during the interrupted window, and overwriting it would destroy their changes — so recovery refuses.","triggerScenarios":"Interrupt a setup, then manually edit the caveman-cloud entry in ~/.claude.json (different command/args) or remove it and install a different caveman-cloud server, then run an agent command that triggers recovery.","commonSituations":"User pointed caveman-cloud at a custom gateway/binary; another caveman version rewrote the registration; the marker file under ~/.caveman-cloud/integrations was deleted or edited while the host config changed.","solutions":["Decide which registration you want: either revert your manual caveman-cloud edits so recovery can complete, or keep them and clear the pending journal to abandon the transaction","Re-run setup after resolving — it will either complete or re-journal from the new state","Avoid editing the caveman-cloud MCP entry by hand; use caveman's own install/uninstall commands so markers stay consistent"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function mcpInKnownState(current: { command: string; args: string[] } | null, options: Array<{ command: string; args: string[] } | null>): boolean {\n  return options.some((o) => o && current && current.command === o.command && JSON.stringify(current.args) === JSON.stringify(o.args));\n}","typeGuard":"function isMcpChangedDuringSetupError(e: unknown): boolean {\n  return e instanceof Error && e.message.includes(\"caveman-cloud MCP changed during interrupted setup\");\n}","tryCatchPattern":"try {\n  runCavemanAgentCommand(\"claude\");\n} catch (e) {\n  if (isMcpChangedDuringSetupError(e)) {\n    // revert manual caveman-cloud edits or clear the pending journal, then re-run\n  } else throw e;\n}","preventionTips":["Never edit the caveman-cloud MCP entry by hand; use caveman install/uninstall commands","Resolve interrupted setups before reconfiguring MCP servers","Keep ~/.caveman-cloud/integrations markers intact"],"tags":["recovery","mcp","safety","agent-native"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}