{"record":{"id":"f88fcdbae9dd5c68","repo":"JuliusBrussee/caveman","slug":"claude-anthropic-base-url-changed-after-enable-re","errorCode":null,"errorMessage":"Claude ANTHROPIC_BASE_URL changed after enable; refusing destructive disable","messagePattern":"Claude ANTHROPIC_BASE_URL changed after enable; refusing destructive disable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7575,"sourceCode":"  const before = nativeBackupBytes(operation);\n  if (!current) {\n    if (operation.before_exists) throw new Error(`${operation.file} was removed after enable; refusing destructive disable`);\n    return null;\n  }\n  if (bytesHash(current) === operation.after_sha256) return before;\n\n  if (operation.kind === \"claude-settings\") {\n    const currentRoot = parseJsonFileObject(operation.file, current);\n    const beforeRoot = parseJsonFileObject(operation.file, before);\n    const currentEnv = currentRoot.env && typeof currentRoot.env === \"object\" && !Array.isArray(currentRoot.env)\n      ? currentRoot.env as Record<string, unknown>\n      : {};\n    const beforeEnv = beforeRoot.env && typeof beforeRoot.env === \"object\" && !Array.isArray(beforeRoot.env)\n      ? beforeRoot.env as Record<string, unknown>\n      : {};\n    const route = operation.owned?.route;\n    if (currentEnv.ANTHROPIC_BASE_URL !== undefined && currentEnv.ANTHROPIC_BASE_URL !== route) {\n      throw new Error(\"Claude ANTHROPIC_BASE_URL changed after enable; refusing destructive disable\");\n    }\n    if (currentEnv.ANTHROPIC_BASE_URL === route) {\n      if (beforeEnv.ANTHROPIC_BASE_URL === undefined) delete currentEnv.ANTHROPIC_BASE_URL;\n      else currentEnv.ANTHROPIC_BASE_URL = beforeEnv.ANTHROPIC_BASE_URL;\n    }\n    // The first-party assertion is only ever written when the user carried no\n    // value of their own, so restore is a plain removal — but only while the\n    // value is still exactly ours; a user's later edit outlives disable.\n    const assume = operation.owned?.assume_first_party;\n    if (typeof assume === \"string\" && currentEnv[CLAUDE_ASSUME_FIRST_PARTY_ENV] === assume) {\n      delete currentEnv[CLAUDE_ASSUME_FIRST_PARTY_ENV];\n    }\n    // Symmetric to the enable-side ENABLE_TOOL_SEARCH write: only withdraw the\n    // value we introduced ourselves and only while it is still untouched, so a\n    // user who set their own afterwards keeps it.\n    if (beforeEnv.ENABLE_TOOL_SEARCH === undefined && currentEnv.ENABLE_TOOL_SEARCH === TOOL_SEARCH_DEFAULT) {\n      delete currentEnv.ENABLE_TOOL_SEARCH;\n    }","sourceCodeStart":7557,"sourceCodeEnd":7593,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/2f49f0e1a352aa810e70056b7930aeb0b3d219b4/packages/cli/src/index.ts#L7557-L7593","documentation":"For claude-settings, disable tolerates env.ANTHROPIC_BASE_URL only when it is absent or exactly the caveman route recorded in the journal (owned.route, e.g. <gateway>/w/claude). Any other value means you repointed Claude while the integration was active; removing the caveman env block would silently clobber your URL, so disable refuses.","triggerScenarios":"`caveman disable claude` after editing ~/.claude/settings.json to set env.ANTHROPIC_BASE_URL to a different proxy or the direct API base while caveman routing was installed.","commonSituations":"Temporarily bypassing caveman by editing settings instead of disabling; switching to another gateway; scripts that manage the base URL in settings.json.","solutions":["If the new URL is wanted: copy it out, delete the ANTHROPIC_BASE_URL key (or set it back to the caveman route), run `caveman disable claude`, then set your URL.","If you meant to keep caveman: restore the route from the journal (owned.route in ~/.caveman/integrations/claude.json) or run `caveman doctor claude --fix`.","For temporary rerouting prefer `caveman wrap claude` sessions over editing enabled settings."],"exampleFix":"// before ~/.claude/settings.json\n\"env\": { \"ANTHROPIC_BASE_URL\": \"https://my-other-proxy\" }\n\n// after - key removed (or restored to the caveman route), then `caveman disable claude`\n\"env\": {}","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'node:fs';\nconst settings = JSON.parse(readFileSync(`${process.env.HOME}/.claude/settings.json`, 'utf8'));\nconst journal = JSON.parse(readFileSync(`${process.env.HOME}/.caveman/integrations/claude.json`, 'utf8'));\nconst route = journal.operations.find((o: any) => o.kind === 'claude-settings')?.owned?.route;\nconst url = settings?.env?.ANTHROPIC_BASE_URL;\nif (url !== undefined && url !== route) { console.error('ANTHROPIC_BASE_URL diverged from the caveman route; revert or delete it before disable'); process.exit(1); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Change routing via caveman commands, not by editing env in settings.json","For temporary rerouting use `caveman wrap claude` instead of editing an enabled install","Before disable, diff env.ANTHROPIC_BASE_URL against the journal's owned.route"],"tags":["claude","base-url","disable","config-drift"],"backgroundTag":"config-drift-refusal","analyzedSha":"2f49f0e1a352aa810e70056b7930aeb0b3d219b4","analyzedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}