{"record":{"id":"2f18a4e8838e04d1","repo":"JuliusBrussee/caveman","slug":"claude-caveman-cloud-mcp-changed-after-setup-refu","errorCode":null,"errorMessage":"Claude caveman-cloud MCP changed after setup; refusing to overwrite user fields","messagePattern":"Claude caveman-cloud MCP changed after setup; refusing to overwrite user fields","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":2560,"sourceCode":"    if (!existingBundle) {\n      if (current && !current.equals(Buffer.from(body))) {\n        throw new Error(`${file} already exists with non-canonical content; refusing to overwrite an unjournaled skill`);\n      }\n      continue;\n    }\n    const previous = previousSkills.get(file);\n    if (current && !current.equals(Buffer.from(body)) && (!previous || bytesHash(current) !== previous.after_sha256)) {\n      throw new Error(`${file} changed after setup; refusing to overwrite user skill edits`);\n    }\n  }\n  if (agent === \"claude\") {\n    const actual = claudeMcpRegistration(\"caveman-cloud\");\n    const marker = readMcpServerMarker(\"claude\", \"caveman-cloud\");\n    if (actual.present && !marker) {\n      throw new Error(\"Claude caveman-cloud MCP exists but is not Caveman-journaled; refusing overwrite\");\n    }\n    if (actual.present && marker && !agentNativeCloudMcpMatches(\"claude\", marker)) {\n      throw new Error(\"Claude caveman-cloud MCP changed after setup; refusing to overwrite user fields\");\n    }\n    return;\n  }\n  const config = fileBytes(join(homedir(), \".codex\", \"config.toml\"))?.toString(\"utf8\") ?? \"\";\n  if (!config.includes(\"[mcp_servers.caveman-cloud]\")) return;\n  if (!readMcpServerMarker(\"codex\", \"caveman-cloud\")) {\n    throw new Error(\"[mcp_servers.caveman-cloud] exists but is not Caveman-journaled; refusing overwrite\");\n  }\n}\n\nfunction installAgentNativeBundleSkills(agent: \"claude\" | \"codex\", journal: AgentNativeBundleJournal): void {\n  const originals = new Map(journal.skills.map((skill) => [skill.file, skill.before_base64]));\n  journal.skills = agentNativeSkillFiles(agent).map(({ file, body }) => {\n    mkdirSync(dirname(file), { recursive: true });\n    atomicWriteFile(file, Buffer.from(body));\n    return {\n      file,\n      before_base64: originals.get(file) ?? null,","sourceCodeStart":2542,"sourceCodeEnd":2578,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L2542-L2578","documentation":"Claude-specific preflight with a marker present: the live ~/.claude.json caveman-cloud registration (command, args, shape) does not match what the marker records. That means someone edited the registration after Caveman installed it — envs, command path, or extra args changed — and setup refuses to overwrite those user fields.","triggerScenarios":"Editing the caveman-cloud entry in ~/.claude.json (e.g. changing the binary path or adding args) after Caveman installed it, then running setup/upgrade; or another Caveman version with a different command shape rewrote it without updating the marker.","commonSituations":"User customizing env or command for a local build; version skew between two caveman installs sharing ~/.claude.json; config sync tools overwriting the entry.","solutions":["Revert your manual edits to the caveman-cloud entry (restore the marker's recorded command/args) so setup can proceed","Or delete both the entry and the marker, then re-run setup for a clean, consistent install","If you need a custom command, keep it under a different server name than caveman-cloud"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function claudeMcpMatchesMarker(): boolean {\n  const cfg = JSON.parse(readFileSync(join(homedir(), \".claude.json\"), \"utf8\"));\n  const live = cfg.mcpServers?.[\"caveman-cloud\"];\n  if (!live) return true;\n  let marker: { command: string; args: string[] } | null = null;\n  try { marker = JSON.parse(readFileSync(markerPath, \"utf8\")); } catch { /* absent handled elsewhere */ }\n  return !marker || (marker.command === live.command && JSON.stringify(marker.args) === JSON.stringify(live.args ?? []));\n}","typeGuard":"function isClaudeMcpDriftError(e: unknown): boolean {\n  return e instanceof Error && e.message === \"Claude caveman-cloud MCP changed after setup; refusing to overwrite user fields\";\n}","tryCatchPattern":"try {\n  runCavemanSetup();\n} catch (e) {\n  if (isClaudeMcpDriftError(e)) {\n    // restore the marker-recorded command/args or delete entry+marker and re-run\n  } else throw e;\n}","preventionTips":["Keep the caveman-cloud entry exactly as caveman wrote it; customize under a different server name","Re-run caveman setup after intentional manual changes so the journal/marker reflect reality"],"tags":["mcp","claude","preflight","safety"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}