JuliusBrussee/caveman · error · Error
Gemini caveman MCP entry changed after enable; refusing dest
Error message
Gemini caveman MCP entry changed after enable; refusing destructive disable
What it means
For gemini-settings, disable first strips managed hooks and then only removes mcpServers.caveman from ~/.gemini/settings.json when it still JSON-matches the entry recorded at enable time (owned.installed_mcp). A modified `caveman` entry is treated as user-owned; deleting it blindly could lose your edits, so the operation aborts with the same refusal family as the Claude MCP guard.
Source
Thrown at packages/cli/src/index.ts:7633
if (Object.keys(servers).length > 0) currentRoot.mcpServers = servers;
else delete currentRoot.mcpServers;
return jsonBytes(currentRoot);
}
if (operation.kind === "codex-hooks") {
return jsonBytes(removeNativeHookEntries(parseJsonFileObject(operation.file, current), "codex"));
}
if (operation.kind === "gemini-settings") {
const currentRoot = removeNativeHookEntries(parseJsonFileObject(operation.file, current), "gemini");
const beforeRoot = parseJsonFileObject(operation.file, before);
const servers = currentRoot.mcpServers && typeof currentRoot.mcpServers === "object" && !Array.isArray(currentRoot.mcpServers)
? currentRoot.mcpServers as Record<string, unknown>
: {};
const beforeServers = beforeRoot.mcpServers && typeof beforeRoot.mcpServers === "object" && !Array.isArray(beforeRoot.mcpServers)
? beforeRoot.mcpServers as Record<string, unknown>
: {};
const installed = operation.owned?.installed_mcp;
if (servers.caveman !== undefined && JSON.stringify(servers.caveman) !== JSON.stringify(installed)) {
throw new Error("Gemini caveman MCP entry changed after enable; refusing destructive disable");
}
if (servers.caveman !== undefined) {
if (beforeServers.caveman === undefined) delete servers.caveman;
else servers.caveman = beforeServers.caveman;
}
if (Object.keys(servers).length > 0) currentRoot.mcpServers = servers;
else delete currentRoot.mcpServers;
return jsonBytes(currentRoot);
}
if (operation.kind === "gemini-env") {
const block = operation.owned?.route_block;
if (typeof block !== "string") throw new Error("Gemini integration journal lacks owned routing block");
const text = current.toString("utf8");
if (text.includes(GEMINI_NATIVE_ENV_BEGIN) && !text.includes(block)) {
throw new Error("Gemini routing block changed after enable; refusing destructive disable");
}
return Buffer.from(text.replace(`${block}\n\n`, "").replace(`\n\n${block}\n`, "\n").replace(`${block}\n`, "").replace(block, ""));
}View on GitHub (pinned to 2f49f0e1a3)
Solutions
- Revert mcpServers.caveman to the journalled value (owned.installed_mcp in ~/.caveman/integrations/gemini.json), then re-run `caveman disable gemini`.
- Or delete the `caveman` key from mcpServers yourself, then re-run disable.
- Keep personal MCP servers under names other than `caveman`.
Example fix
// before ~/.gemini/settings.json
"mcpServers": { "caveman": { "type": "stdio", "command": "./wrapper" } }
// after - journalled entry restored (or key removed), then `caveman disable gemini`
"mcpServers": { "caveman": { "type": "stdio", "command": "/usr/local/bin/caveman-mcp", "args": [], "env": {} } } Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'node:fs';
const s = JSON.parse(readFileSync(`${process.env.HOME}/.gemini/settings.json`, 'utf8'));
const journal = JSON.parse(readFileSync(`${process.env.HOME}/.caveman/integrations/gemini.json`, 'utf8'));
const installed = journal.operations.find((o: any) => o.kind === 'gemini-settings')?.owned?.installed_mcp;
if (s?.mcpServers?.caveman !== undefined && JSON.stringify(s.mcpServers.caveman) !== JSON.stringify(installed)) { console.error('Gemini mcpServers.caveman drifted from journal; revert or remove it before disable'); process.exit(1); } Prevention
- Treat mcpServers.caveman in ~/.gemini/settings.json as read-only managed state
- Use a different server name for personal MCP entries
- Run `caveman doctor gemini --fix` after any MCP reconfiguration
When it happens
Trigger: `caveman disable gemini` after editing the `caveman` entry in mcpServers of ~/.gemini/settings.json - changed command, added args/env, or reformatted by another MCP-config tool.
Common situations: Pointing the MCP command at a wrapper; moving the caveman-mcp binary; tools that rewrite settings.json and alter key order or formatting.
Related errors
- Claude caveman MCP entry changed after enable; refusing dest
- Gemini routing block changed after enable; refusing destruct
- OpenCode caveman MCP entry changed after enable; refusing de
- Hermes ${key.replace("_block", "")} block changed after enab
- Claude caveman-cloud MCP changed after setup; refusing to ov
AI-assisted analysis of JuliusBrussee/caveman@2f49f0e1a3 (2026-08-18).
Data as JSON: /api/errors/89ba1a2bafadf3aa.
Report an issue: GitHub.