JuliusBrussee/caveman · error · Error
Gemini routing block changed after enable; refusing destruct
Error message
Gemini routing block changed after enable; refusing destructive disable
What it means
For gemini-env, restore only proceeds when the file either no longer contains the begin marker '# >>> caveman:native-routing' (block already gone) or still contains the exact journalled owned.route_block. If the marker is present but the exact block string is not, the routing block was edited after enable; removing by marker boundaries could eat your changes, so disable refuses.
Source
Thrown at packages/cli/src/index.ts:7648
: {};
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, ""));
}
if (operation.kind === "opencode-plugin" || operation.kind === "pi-extension") {
throw new Error(`${operation.file} changed after enable; refusing destructive disable`);
}
if (operation.kind === "opencode-config") {
const root = parseJsonFileObject(operation.file, current);
const providers = root.provider && typeof root.provider === "object" && !Array.isArray(root.provider) ? root.provider as Record<string, unknown> : {};
const routes = operation.owned?.routes && typeof operation.owned.routes === "object" && !Array.isArray(operation.owned.routes) ? operation.owned.routes as Record<string, unknown> : {};
const previousRoutes = operation.owned?.previous_routes && typeof operation.owned.previous_routes === "object" && !Array.isArray(operation.owned.previous_routes) ? operation.owned.previous_routes as Record<string, unknown> : {};
for (const providerID of ["openai", "anthropic"]) {
const provider = providers[providerID] && typeof providers[providerID] === "object" && !Array.isArray(providers[providerID]) ? providers[providerID] as Record<string, unknown> : {};
const options = provider.options && typeof provider.options === "object" && !Array.isArray(provider.options) ? provider.options as Record<string, unknown> : {};
if (options.baseURL !== undefined && options.baseURL !== routes[providerID]) {
throw new Error(`OpenCode ${providerID} baseURL changed after enable; refusing destructive disable`);
}
if (previousRoutes[providerID] === null || previousRoutes[providerID] === undefined) delete options.baseURL;View on GitHub (pinned to 2f49f0e1a3)
Solutions
- Restore the block bytes from the journal: owned.route_block of the gemini-env operation in ~/.caveman/integrations/gemini.json.
- Or delete the entire marked block (from '# >>> caveman:native-routing' to its '# <<< ...' end) by hand, then re-run `caveman disable gemini` - with the marker gone the guard passes.
- Prefer `caveman doctor gemini --fix` to re-canonicalize the block before disabling.
Example fix
# before ~/.gemini/.env (block hand-edited, no longer byte-exact) # >>> caveman:native-routing export GEMINI_API_BASE="https://my-tweak" # edited line # <<< caveman:native-routing # after - block removed entirely by hand (or restored from journal), then $ caveman disable gemini
Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'node:fs';
const env = readFileSync(`${process.env.HOME}/.gemini/.env`, 'utf8');
if (env.includes('# >>> caveman:native-routing')) {
const journal = JSON.parse(readFileSync(`${process.env.HOME}/.caveman/integrations/gemini.json`, 'utf8'));
const block = journal.operations.find((o: any) => o.kind === 'gemini-env')?.owned?.route_block;
if (typeof block === 'string' && !env.includes(block)) { console.error('gemini routing block drifted; restore it or remove the whole marked block before disable'); process.exit(1); }
} Prevention
- Edit gateway routing through caveman config, never inside the marked .env block
- Point formatters and secret injectors away from ~/.gemini/.env
- Run `caveman doctor gemini --fix` before disable to re-canonicalize the block
When it happens
Trigger: `caveman disable gemini` after editing lines inside the caveman routing block in ~/.gemini/.env - changed the base URL, reformatted, added or reordered lines - so the file no longer contains the byte-exact block recorded in the journal.
Common situations: Hand-tweaking the gateway URL inside the marked block instead of via caveman config; formatters or secret-injection tools rewriting .env files.
Related errors
- Gemini caveman MCP entry changed after enable; refusing dest
- ${agent} ${event} Caveman hook changed after enable; refusin
- ${operation.file} was removed after enable; refusing destruc
- Claude ANTHROPIC_BASE_URL changed after enable; refusing des
- Claude caveman MCP entry changed after enable; refusing dest
AI-assisted analysis of JuliusBrussee/caveman@2f49f0e1a3 (2026-08-18).
Data as JSON: /api/errors/26a536b05e4d0e75.
Report an issue: GitHub.