JuliusBrussee/caveman · error
Claude ANTHROPIC_BASE_URL changed after enable; refusing…
Error message
Claude ANTHROPIC_BASE_URL changed after enable; refusing destructive disable
What it means
When disabling the Claude integration, the CLI restores the `ANTHROPIC_BASE_URL` env entry in the Claude settings. If the current value exists but differs from the route caveman wrote at enable (`operation.owned.route`), the user has changed their API routing after enable. Overwriting it would destroy the user's own configuration, so the CLI refuses the destructive disable.
Solutions
- Set `ANTHROPIC_BASE_URL` in the Claude settings back to the exact route recorded at enable (see journal `owned.route`), then retry disable.
- If the new base URL is intentional, remove the caveman env entry manually from `~/.claude/settings.json` instead of using disable.
- Check for a second enable targeting a different gateway; disable with the journal that matches the current route.
- Inspect the journal file to confirm `owned.route` and diff against the settings env block.
Example fix
// before (~/.claude/settings.json)
{ "env": { "ANTHROPIC_BASE_URL": "https://my-proxy.example.com" } }
// after (restore caveman route, then disable)
{ "env": { "ANTHROPIC_BASE_URL": "https://gateway.caveman.local/v1" } } Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from "node:fs";
const settings = JSON.parse(readFileSync("~/.claude/settings.json", "utf8"));
const route = journal.operations.find(o => o.owned?.route)?.owned?.route;
const cur = settings.env?.ANTHROPIC_BASE_URL;
if (cur !== undefined && cur !== route) {
console.error(`ANTHROPIC_BASE_URL drifted: ${cur} (journal expects ${route})`);
} Prevention
- Disable the caveman integration before switching API providers or proxy URLs.
- Record journal `owned.route` and compare whenever ANTHROPIC_BASE_URL is edited.
- Keep provider changes in a separate env layer rather than editing Claude settings directly.
When it happens
Trigger: `restoreNativeOperation` for a `claude-settings` operation where `currentEnv.ANTHROPIC_BASE_URL !== undefined && currentEnv.ANTHROPIC_BASE_URL !== operation.owned.route`.
Common situations: The user switched API providers/proxies (e.g. different gateway URL) while caveman routing was enabled; an upstream tool rewrote ANTHROPIC_BASE_URL; switching between caveman and another router.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Claude caveman MCP entry changed after enable; refusing…
- Caveman hook changed after enable; refusing destructive…
- Gemini caveman MCP entry changed after enable; refusing…
- Gemini routing block changed after enable; refusing…
- changed after enable; refusing destructive disable
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/f88fcdbae9dd5c68.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:8270
const before = nativeBackupBytes(operation);
if (!current) {
if (operation.before_exists) throw new Error(`${operation.file} was removed after enable; refusing destructive disable`);
return null;
}
if (bytesHash(current) === operation.after_sha256) return before;
if (operation.kind === "claude-settings") {
const currentRoot = parseJsonFileObject(operation.file, current);
const beforeRoot = parseJsonFileObject(operation.file, before);
const currentEnv = currentRoot.env && typeof currentRoot.env === "object" && !Array.isArray(currentRoot.env)
? currentRoot.env as Record<string, unknown>
: {};
const beforeEnv = beforeRoot.env && typeof beforeRoot.env === "object" && !Array.isArray(beforeRoot.env)
? beforeRoot.env as Record<string, unknown>
: {};
const route = operation.owned?.route;
if (currentEnv.ANTHROPIC_BASE_URL !== undefined && currentEnv.ANTHROPIC_BASE_URL !== route) {
throw new Error("Claude ANTHROPIC_BASE_URL changed after enable; refusing destructive disable");
}
if (currentEnv.ANTHROPIC_BASE_URL === route) {
if (beforeEnv.ANTHROPIC_BASE_URL === undefined) delete currentEnv.ANTHROPIC_BASE_URL;
else currentEnv.ANTHROPIC_BASE_URL = beforeEnv.ANTHROPIC_BASE_URL;
}
// The first-party assertion is only ever written when the user carried no
// value of their own, so restore is a plain removal — but only while the
// value is still exactly ours; a user's later edit outlives disable.
const assume = operation.owned?.assume_first_party;
if (typeof assume === "string" && currentEnv[CLAUDE_ASSUME_FIRST_PARTY_ENV] === assume) {
delete currentEnv[CLAUDE_ASSUME_FIRST_PARTY_ENV];
}
// Symmetric to the enable-side ENABLE_TOOL_SEARCH write: only withdraw the
// value we introduced ourselves and only while it is still untouched, so a
// user who set their own afterwards keeps it.
if (beforeEnv.ENABLE_TOOL_SEARCH === undefined && currentEnv.ENABLE_TOOL_SEARCH === TOOL_SEARCH_DEFAULT) {
delete currentEnv.ENABLE_TOOL_SEARCH;
}View on GitHub (pinned to 3ee70a1026)