JuliusBrussee/caveman · error
Codex integration journal lacks owned blocks
Error message
Codex integration journal lacks owned blocks
What it means
Codex (TOML) disable requires the journal to contain both root_block and tables_block strings, which mark the Caveman-generated sections. If either is missing, rollback is impossible and the tool throws immediately, before reading the config. Journal-state completeness is a precondition for any Codex rollback.
Solutions
- Manually remove the Caveman blocks (delimited by CODEX_NATIVE_ROOT_BEGIN / CODEX_NATIVE_TABLES_BEGIN markers) from the Codex TOML config.
- Re-run enable to regenerate the journal with both blocks, then disable.
- Align CLI versions between enable and disable so the journal schema matches.
Example fix
// missing journal state
{ "owned": { "tables_block": "..." } } // root_block absent
// after re-enable
{ "owned": { "root_block": "# caveman root ...", "tables_block": "# caveman tables ..." } } Defensive patterns
Strategy: validation
Validate before calling
const owned = journal.owned ?? {};
if (typeof owned.root_block !== 'string' || typeof owned.tables_block !== 'string') {
throw new Error('Codex journal incomplete; re-enable before disabling');
} Type guard
const hasCodexBlocks = (owned) => typeof owned?.root_block === 'string' && owned.root_block.length > 0 && typeof owned?.tables_block === 'string' && owned.tables_block.length > 0;
Try / catch
try {
await caveman.disable(['codex']);
} catch (err) {
if (err.message.includes('journal lacks owned blocks')) {
console.error('Journal missing; manually strip Caveman blocks from the Codex TOML config.');
} else throw err;
} Prevention
- Keep Caveman state/journal files intact on the machine that ran enable.
- Use the same CLI version for enable and disable.
- Back up config.toml before enabling.
- Re-enable to regenerate the journal if it was lost or corrupted.
When it happens
Trigger: Running disable on a Codex config operation when operation.owned.root_block or operation.owned.tables_block is undefined/not a string — journal never written, written by an older version, or corrupted.
Common situations: CLI upgrade changed the Codex journal schema; state file truncated; user deleted journal files while keeping the modified Codex config.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Aider integration journal lacks owned blocks
- Hermes integration journal lacks owned routing block
- : MCP ownership journal failed; native config may already…
- MCP transaction journal already exists; refusing overwrite
- MCP transaction journals disagree; refusing recovery
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/903237a709d5f9c4.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:8422
let text = current.toString("utf8");
const routeBlock = operation.owned?.route_block;
const previousRouteLines = operation.owned?.previous_route_lines;
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;
}
}
View on GitHub (pinned to 3ee70a1026)