{"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":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":8340,"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":8322,"sourceCodeEnd":8358,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L8322-L8358","documentation":"For a `gemini-env` journal operation, the CLI needs the exact routed env block text it wrote at enable, stored in `operation.owned.route_block`. If that field is missing or not a string, the journal is malformed/incomplete and the CLI cannot safely strip the block from the Gemini env file, so it throws an internal-invariant error rather than guessing.","triggerScenarios":"`restoreNativeOperation` for `operation.kind === 'gemini-env'` where `typeof operation.owned?.route_block !== 'string'`.","commonSituations":"The journal file was hand-edited or truncated; a caveman version wrote an older journal schema that lacks `route_block`; the journal was copied from another machine with fields stripped.","solutions":["Manually remove the caveman env block (between `GEMINI_NATIVE_ENV_BEGIN`/end markers) from the Gemini settings file, then delete the journal.","Regenerate or repair the journal so `owned.route_block` contains the exact block text written at enable, then retry disable.","Re-enable and immediately disable with the same caveman version so a fresh, complete journal is written.","Check the journal schema version; upgrade/downgrade caveman to the version that matches the journal format."],"exampleFix":"// inspect the journal\njq '.operations[] | select(.kind == \"gemini-env\") | .owned' ~/.caveman/journal.json\n// if route_block is null, strip the block manually then remove the operation entry","handlingStrategy":"validation","validationCode":"const op = journal.operations.find(o => o.kind === \"gemini-env\");\nif (!op || typeof op.owned?.route_block !== \"string\") {\n  console.error(\"journal gemini-env operation lacks owned.route_block; manual cleanup required\");\n}","typeGuard":"function hasRouteBlock(op) {\n  return op.kind === \"gemini-env\"\n    && typeof op.owned === \"object\" && op.owned !== null\n    && typeof op.owned.route_block === \"string\";\n}","tryCatchPattern":"try {\n  restoreGeminiEnv(operation);\n} catch (err) {\n  if (String(err).includes(\"lacks owned routing block\")) {\n    // fall back to manual block removal + journal cleanup\n  }\n}","preventionTips":["Never hand-edit or truncate the caveman journal file.","Keep journal schema and caveman CLI versions in sync.","Validate journal JSON with the expected schema after copying between machines."],"tags":["journal","gemini","invariant","disable","schema"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}