JuliusBrussee/caveman · error · Error

Gemini integration journal lacks owned routing block

Error message

Gemini integration journal lacks owned routing block

What it means

The gemini-env operation removes routing by exact string: it deletes the owned.route_block that was recorded in the journal when ~/.gemini/.env was written. A journal whose gemini-env operation lacks a string route_block (older schema, foreign writer, hand edit) cannot drive restoration, so restoreNativeOperation() refuses immediately - even before looking at the env file.

Source

Thrown at packages/cli/src/index.ts:7645

      : {};
    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, ""));
  }
  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]) {

View on GitHub (pinned to 2f49f0e1a3)

Solutions

  1. Upgrade caveman on this machine and run `caveman doctor gemini --fix` so the journal is repaired or rewritten by the current version.
  2. If unrecoverable, remove the '# >>> caveman:native-routing' block from ~/.gemini/.env by hand, delete ~/.caveman/integrations/gemini.json (and .pending-gemini.json if present), then `caveman enable gemini` fresh.
  3. Never hand-edit files under ~/.caveman/integrations.
Defensive patterns

Strategy: type-guard

Type guard

import { readFileSync } from 'node:fs';
function geminiEnvHasRouteBlock(home = process.env.HOME!): boolean {
  try {
    const journal = JSON.parse(readFileSync(`${home}/.caveman/integrations/gemini.json`, 'utf8'));
    const op = (journal?.operations as any[] | undefined)?.find((o) => o?.kind === 'gemini-env');
    return typeof op?.owned?.route_block === 'string';
  } catch { return false; }
}
if (!geminiEnvHasRouteBlock()) { console.error('gemini journal lacks owned.route_block; upgrade caveman and run doctor gemini --fix'); process.exit(1); }

Try / catch

try {
  await runCaveman('disable', 'gemini');
} catch (e) {
  if (/journal lacks owned routing block/.test(String(e))) {
    // journal predates the current schema: upgrade caveman, run 'caveman doctor gemini --fix', then retry
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: `caveman disable gemini` when ~/.caveman/integrations/gemini.json contains a gemini-env operation whose owned.route_block is missing or not a string - a journal from an older caveman version or one edited by hand.

Common situations: Upgrading caveman across journal-schema changes; users 'fixing' journals after reading docs; restoring ~/.caveman from a mixed-version backup.

Related errors


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