{"record":{"id":"26a536b05e4d0e75","repo":"JuliusBrussee/caveman","slug":"gemini-routing-block-changed-after-enable-refusin","errorCode":null,"errorMessage":"Gemini routing block changed after enable; refusing destructive disable","messagePattern":"Gemini routing block changed after enable; refusing destructive disable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":8343,"sourceCode":"      : {};\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]) {\n        throw new Error(`OpenCode ${providerID} baseURL changed after enable; refusing destructive disable`);\n      }\n      if (previousRoutes[providerID] === null || previousRoutes[providerID] === undefined) delete options.baseURL;","sourceCodeStart":8325,"sourceCodeEnd":8361,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L8325-L8361","documentation":"When removing the `gemini-env` routing block, the CLI checks that the current file still contains a Gemini native env marker (`GEMINI_NATIVE_ENV_BEGIN`) only if the expected owned block is present. If markers exist but the recorded `route_block` text does not, the block was edited after enable, and removing markers or mismatched text would corrupt the file, so the disable is refused as destructive.","triggerScenarios":"`restoreNativeOperation` for `operation.kind === 'gemini-env'` where `text.includes(GEMINI_NATIVE_ENV_BEGIN) && !text.includes(block)` — i.e. the env file still has caveman env markers but not the exact block recorded in the journal.","commonSituations":"The user hand-edited lines inside the caveman env block (changed model, base URL, keys); a different caveman version rewrote the block with different formatting; another tool inserted a similar env block.","solutions":["Restore the env block to the exact text in the journal's `owned.route_block`, then retry disable.","Manually delete the entire caveman env block (from the BEGIN marker to the END marker) from the Gemini settings, then remove the journal operation.","Run `caveman doctor gemini --fix` to rewrite the block to the canonical form, then disable.","Diff the current file against the journal's route_block to find the edited line and revert it."],"exampleFix":"// before (edited block, marker present, text differs)\n# BEGIN caveman env\nexport ANTHROPIC_BASE_URL=\"https://other-gateway/v1\"\n# END caveman env\n\n// after (restore recorded route, then disable)\n# BEGIN caveman env\nexport ANTHROPIC_BASE_URL=\"https://gateway.caveman.local/v1\"\n# END caveman env","handlingStrategy":"validation","validationCode":"import { readFileSync } from \"node:fs\";\nconst text = readFileSync(\"~/.gemini/settings.json\", \"utf8\");\nconst block = journal.operations.find(o => o.kind === \"gemini-env\")?.owned?.route_block;\nif (text.includes(\"# BEGIN caveman\") && block && !text.includes(block)) {\n  console.error(\"Gemini env block edited after enable; restore or remove it manually before disable\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat the marked caveman env block as owned; edit surrounding content only.","Re-run enable rather than editing block contents (model, base URL) in place.","Diff the file against the journal's route_block after any tool touches Gemini settings."],"tags":["gemini","env-block","safety-guard","disable","config-drift"],"backgroundTag":"invalid-state-transition","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"}