{"record":{"id":"19998dd9472140a2","repo":"JuliusBrussee/caveman","slug":"gemini-integration-journal-lacks-owned-routing-blo","errorCode":null,"errorMessage":"Gemini integration journal lacks owned routing block","messagePattern":"Gemini integration journal lacks owned routing block","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7645,"sourceCode":"      : {};\n    const beforeServers = beforeRoot.mcpServers && typeof beforeRoot.mcpServers === \"object\" && !Array.isArray(beforeRoot.mcpServers)\n      ? beforeRoot.mcpServers as Record<string, unknown>\n      : {};\n    const installed = operation.owned?.installed_mcp;\n    if (servers.caveman !== undefined && JSON.stringify(servers.caveman) !== JSON.stringify(installed)) {\n      throw new Error(\"Gemini caveman MCP entry changed after enable; refusing destructive disable\");\n    }\n    if (servers.caveman !== undefined) {\n      if (beforeServers.caveman === undefined) delete servers.caveman;\n      else servers.caveman = beforeServers.caveman;\n    }\n    if (Object.keys(servers).length > 0) currentRoot.mcpServers = servers;\n    else delete currentRoot.mcpServers;\n    return jsonBytes(currentRoot);\n  }\n  if (operation.kind === \"gemini-env\") {\n    const block = operation.owned?.route_block;\n    if (typeof block !== \"string\") throw new Error(\"Gemini integration journal lacks owned routing block\");\n    const text = current.toString(\"utf8\");\n    if (text.includes(GEMINI_NATIVE_ENV_BEGIN) && !text.includes(block)) {\n      throw new Error(\"Gemini routing block changed after enable; refusing destructive disable\");\n    }\n    return Buffer.from(text.replace(`${block}\\n\\n`, \"\").replace(`\\n\\n${block}\\n`, \"\\n\").replace(`${block}\\n`, \"\").replace(block, \"\"));\n  }\n  if (operation.kind === \"opencode-plugin\" || operation.kind === \"pi-extension\") {\n    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);\n  }\n  if (operation.kind === \"opencode-config\") {\n    const root = parseJsonFileObject(operation.file, current);\n    const providers = root.provider && typeof root.provider === \"object\" && !Array.isArray(root.provider) ? root.provider as Record<string, unknown> : {};\n    const routes = operation.owned?.routes && typeof operation.owned.routes === \"object\" && !Array.isArray(operation.owned.routes) ? operation.owned.routes as Record<string, unknown> : {};\n    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> : {};\n    for (const providerID of [\"openai\", \"anthropic\"]) {\n      const provider = providers[providerID] && typeof providers[providerID] === \"object\" && !Array.isArray(providers[providerID]) ? providers[providerID] as Record<string, unknown> : {};\n      const options = provider.options && typeof provider.options === \"object\" && !Array.isArray(provider.options) ? provider.options as Record<string, unknown> : {};\n      if (options.baseURL !== undefined && options.baseURL !== routes[providerID]) {","sourceCodeStart":7627,"sourceCodeEnd":7663,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/2f49f0e1a352aa810e70056b7930aeb0b3d219b4/packages/cli/src/index.ts#L7627-L7663","documentation":"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.","triggerScenarios":"`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.","commonSituations":"Upgrading caveman across journal-schema changes; users 'fixing' journals after reading docs; restoring ~/.caveman from a mixed-version backup.","solutions":["Upgrade caveman on this machine and run `caveman doctor gemini --fix` so the journal is repaired or rewritten by the current version.","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.","Never hand-edit files under ~/.caveman/integrations."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"import { readFileSync } from 'node:fs';\nfunction geminiEnvHasRouteBlock(home = process.env.HOME!): boolean {\n  try {\n    const journal = JSON.parse(readFileSync(`${home}/.caveman/integrations/gemini.json`, 'utf8'));\n    const op = (journal?.operations as any[] | undefined)?.find((o) => o?.kind === 'gemini-env');\n    return typeof op?.owned?.route_block === 'string';\n  } catch { return false; }\n}\nif (!geminiEnvHasRouteBlock()) { console.error('gemini journal lacks owned.route_block; upgrade caveman and run doctor gemini --fix'); process.exit(1); }","tryCatchPattern":"try {\n  await runCaveman('disable', 'gemini');\n} catch (e) {\n  if (/journal lacks owned routing block/.test(String(e))) {\n    // journal predates the current schema: upgrade caveman, run 'caveman doctor gemini --fix', then retry\n    throw e;\n  }\n  throw e;\n}","preventionTips":["Upgrade caveman before disabling integrations created by much older versions","Never hand-edit integration journals","Keep ~/.caveman and the caveman binary versioned together in provisioning"],"tags":["gemini","journal","schema","disable"],"backgroundTag":"journal-missing-owned-metadata","analyzedSha":"2f49f0e1a352aa810e70056b7930aeb0b3d219b4","analyzedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}