JuliusBrussee/caveman · error · Error
Codex Caveman config block changed after enable; refusing de
Error message
Codex Caveman config block changed after enable; refusing destructive disable
What it means
Thrown while disabling or repairing Codex when config.toml drifted after enable and, for either owned block, the Caveman begin marker ('# >>> caveman:native-root' or '# >>> caveman:native-tables') is still present in the file but the exact journaled block content is not. Caveman strips its blocks verbatim on disable; if the content between the markers was edited, a blind removal could delete your edits, so it refuses. Note the asymmetry: if the whole marker is absent, the block is treated as already removed and the disable proceeds.
Source
Thrown at packages/cli/src/index.ts:7730
if (typeof routeBlock !== "string" || !Array.isArray(previousRouteLines)) {
throw new Error("Hermes integration journal lacks owned routing block");
}
if (!text.includes(routeBlock)) throw new Error("Hermes routing block changed after enable; refusing destructive disable");
text = text.replace(routeBlock, previousRouteLines.filter((line): line is string => typeof line === "string").join("\n"));
for (const key of ["plugin_block", "mcp_block"] as const) {
const block = operation.owned?.[key];
if (block === null || block === undefined) continue;
if (typeof block !== "string" || !text.includes(block)) throw new Error(`Hermes ${key.replace("_block", "")} block changed after enable; refusing destructive disable`);
text = text.replace(block, "");
}
return Buffer.from(text);
}
const rootBlock = operation.owned?.root_block;
const tablesBlock = operation.owned?.tables_block;
if (typeof rootBlock !== "string" || typeof tablesBlock !== "string") throw new Error("Codex integration journal lacks owned blocks");
let text = current.toString("utf8");
for (const [block, begin] of [[rootBlock, CODEX_NATIVE_ROOT_BEGIN], [tablesBlock, CODEX_NATIVE_TABLES_BEGIN]] as const) {
if (text.includes(begin) && !text.includes(block)) throw new Error("Codex Caveman config block changed after enable; refusing destructive disable");
text = text.replace(`${block}\n\n`, "").replace(`\n\n${block}\n`, "\n").replace(block, "");
}
return Buffer.from(text);
}
function writeNativeRestoration(file: string, bytes: Buffer | null): void {
if (bytes) {
atomicWriteFile(file, bytes);
return;
}
try { unlinkSync(file); } catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
}
function restoreNativeJournalFiles(journal: NativeJournal): Array<{ file: string; bytes: Buffer | null }> {
// Resolve every merge/conflict before first write. A conflict therefore leaves
// all host files and the journal byte-identical.View on GitHub (pinned to 2f49f0e1a3)
Solutions
- Restore the exact block text from owned.root_block / owned.tables_block of ~/.caveman/integrations/codex.json (keep everything between the markers identical), then rerun `caveman disable codex`
- Run `caveman doctor codex` first to confirm which block drifted
- If you want to keep the edits: move them out of the caveman blocks first, then disable; or clean up manually and delete the journal
- Re-run `caveman enable codex` and disable in the same session before any edits
Example fix
# before: ~/.codex/config.toml, caveman block edited after enable # >>> caveman:native-root model = 'gpt-5-mini' # changed from journaled value # after: block matches owned.root_block exactly, then `caveman disable codex` succeeds
Defensive patterns
Strategy: try-catch
Validate before calling
// Marker present but content changed is the exact trigger — check both
import { readFileSync } from 'node:fs';
const home = process.env.CAVEMAN_HOME ?? `${process.env.HOME}/.caveman`;
const j = JSON.parse(readFileSync(`${home}/integrations/codex.json`, 'utf8'));
const text = readFileSync(`${process.env.HOME}/.codex/config.toml`, 'utf8');
for (const [block, begin] of [['root_block', '# >>> caveman:native-root'], ['tables_block', '# >>> caveman:native-tables']]) {
const owned = j.operations[0]?.owned?.[block];
if (text.includes(begin) && !text.includes(owned)) console.error(`${block} drifted`);
} Try / catch
catch (err) { if (err.message.includes('Codex Caveman config block changed after enable')) { /* restore the exact block text between the caveman markers, or move your edits outside the markers first; retry once */ } else throw err; } Prevention
- Make config.toml edits outside the '# >>> caveman:native-*' marker blocks only
- Run `caveman doctor codex` before scripted disable to see drift
- Re-run `caveman enable codex` after intentional edits to re-stamp the journal
When it happens
Trigger: Editing lines inside the Caveman-managed blocks of ~/.codex/config.toml after `caveman enable codex` — e.g. changing the model provider baseURL or profile tables Caveman wrote — then running `caveman disable codex` or `caveman doctor codex --fix`.
Common situations: Tweaking the routed model or provider settings that live inside the caveman block; another codex config tool rewriting config.toml with different formatting; resolving a git dotfiles conflict by editing inside the block.
Related errors
- Hermes routing block changed after enable; refusing destruct
- [mcp_servers.caveman-cloud] exists but is not Caveman-journa
- ${agent} ${event} Caveman hook changed after enable; refusin
- ${operation.file} was removed after enable; refusing destruc
- Claude ANTHROPIC_BASE_URL changed after enable; refusing des
AI-assisted analysis of JuliusBrussee/caveman@2f49f0e1a3 (2026-08-18).
Data as JSON: /api/errors/e954652c373dc3fc.
Report an issue: GitHub.