JuliusBrussee/caveman · error
Gemini caveman MCP entry changed after enable; refusing…
Error message
Gemini caveman MCP entry changed after enable; refusing destructive disable
What it means
Same ownership check as the Claude case but for Gemini: on disable, the CLI verifies `mcpServers.caveman` in the Gemini settings matches the JSON caveman installed at enable (`operation.owned.installed_mcp`). A divergent entry means the config was modified after enable, so the CLI refuses to destructively remove it.
Solutions
- Restore `mcpServers.caveman` to the exact object from the journal's `owned.installed_mcp`, then retry disable.
- Manually remove the `mcpServers.caveman` key from the Gemini settings file, then re-run disable.
- Run `caveman doctor gemini --fix` to reconcile the entry with the journal, then disable.
- Remove the stale journal operation so disable stops asserting ownership over the modified entry.
Example fix
// before (~/.gemini/settings.json)
"mcpServers": { "caveman": { "command": "node", "args": ["/custom/path/caveman-mcp.js"] } }
// after (restore recorded form)
"mcpServers": { "caveman": { "command": "caveman", "args": ["mcp"] } } Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from "node:fs";
const settings = JSON.parse(readFileSync("~/.gemini/settings.json", "utf8"));
const op = journal.operations.find(o => o.owned?.installed_mcp);
if (settings.mcpServers?.caveman &&
JSON.stringify(settings.mcpServers.caveman) !== JSON.stringify(op.owned.installed_mcp)) {
console.error("Gemini mcpServers.caveman drifted from journal; restore before disable");
} Prevention
- Avoid hand-editing the caveman MCP entry in Gemini settings.
- Re-run enable (which rewrites the entry) instead of patching it in place.
- Verify the entry matches the journal after any Gemini CLI update that touches mcpServers.
When it happens
Trigger: `restoreNativeOperation` for a Gemini settings operation where `servers.caveman !== undefined` and `JSON.stringify(servers.caveman) !== JSON.stringify(operation.owned.installed_mcp)`.
Common situations: Manual edits to the Gemini settings `mcpServers` block; Gemini CLI or another installer rewrote the caveman entry; caveman version upgrade changed the MCP entry definition.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Claude caveman MCP entry changed after enable; refusing…
- Gemini routing block changed after enable; refusing…
- Caveman hook changed after enable; refusing destructive…
- caveman-cloud MCP changed after setup; refusing destructive…
- caveman-cloud MCP postflight mismatch
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/89ba1a2bafadf3aa.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:8328
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 3ee70a1026)