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

  1. Restore the block bytes from the journal: owned.route_block of the gemini-env operation in ~/.caveman/integrations/gemini.json.
  2. 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.
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@2f49f0e1a3 (2026-08-18). Data as JSON: /api/errors/26a536b05e4d0e75. Report an issue: GitHub.