{"record":{"id":"903237a709d5f9c4","repo":"JuliusBrussee/caveman","slug":"codex-integration-journal-lacks-owned-blocks","errorCode":null,"errorMessage":"Codex integration journal lacks owned blocks","messagePattern":"Codex integration journal lacks owned blocks","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7727,"sourceCode":"    let text = current.toString(\"utf8\");\n    const routeBlock = operation.owned?.route_block;\n    const previousRouteLines = operation.owned?.previous_route_lines;\n    if (typeof routeBlock !== \"string\" || !Array.isArray(previousRouteLines)) {\n      throw new Error(\"Hermes integration journal lacks owned routing block\");\n    }\n    if (!text.includes(routeBlock)) throw new Error(\"Hermes routing block changed after enable; refusing destructive disable\");\n    text = text.replace(routeBlock, previousRouteLines.filter((line): line is string => typeof line === \"string\").join(\"\\n\"));\n    for (const key of [\"plugin_block\", \"mcp_block\"] as const) {\n      const block = operation.owned?.[key];\n      if (block === null || block === undefined) continue;\n      if (typeof block !== \"string\" || !text.includes(block)) throw new Error(`Hermes ${key.replace(\"_block\", \"\")} block changed after enable; refusing destructive disable`);\n      text = text.replace(block, \"\");\n    }\n    return Buffer.from(text);\n  }\n  const rootBlock = operation.owned?.root_block;\n  const tablesBlock = operation.owned?.tables_block;\n  if (typeof rootBlock !== \"string\" || typeof tablesBlock !== \"string\") throw new Error(\"Codex integration journal lacks owned blocks\");\n  let text = current.toString(\"utf8\");\n  for (const [block, begin] of [[rootBlock, CODEX_NATIVE_ROOT_BEGIN], [tablesBlock, CODEX_NATIVE_TABLES_BEGIN]] as const) {\n    if (text.includes(begin) && !text.includes(block)) throw new Error(\"Codex Caveman config block changed after enable; refusing destructive disable\");\n    text = text.replace(`${block}\\n\\n`, \"\").replace(`\\n\\n${block}\\n`, \"\\n\").replace(block, \"\");\n  }\n  return Buffer.from(text);\n}\n\nfunction writeNativeRestoration(file: string, bytes: Buffer | null): void {\n  if (bytes) {\n    atomicWriteFile(file, bytes);\n    return;\n  }\n  try { unlinkSync(file); } catch (error) {\n    if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n  }\n}\n","sourceCodeStart":7709,"sourceCodeEnd":7745,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/2f49f0e1a352aa810e70056b7930aeb0b3d219b4/packages/cli/src/index.ts#L7709-L7745","documentation":"The Codex integration journal (~/.caveman/integrations/codex.json) has a codex-config (TOML) operation whose owned metadata lacks root_block or tables_block as strings — the exact config.toml snippets Caveman inserted between its '# >>> caveman:native-root' / '# >>> caveman:native-tables' markers. Without them the disable cannot excise its own additions from ~/.codex/config.toml, so it aborts. Cause: journal written by an older schema, truncated, or hand-edited.","triggerScenarios":"`caveman disable codex` or `caveman doctor codex --fix` with a journal whose operations[].owned.root_block / tables_block are missing or non-string — typically after a Caveman version change or manual journal tampering.","commonSituations":"Downgrading Caveman across a journal schema change; a crashed enable; cleaning up ~/.caveman by hand and corrupting the JSON.","solutions":["Re-run `caveman enable codex` to write a schema-current journal over the live config, then `caveman disable codex`","Use the Caveman version that performed the enable to run the disable","Manual path: delete everything between (and including) the caveman marker comments in ~/.codex/config.toml, then remove the journal"],"exampleFix":"# before: journal lacks owned blocks\ncaveman disable codex\n# >> Codex integration journal lacks owned blocks\n\n# after: re-stamp then disable\ncaveman enable codex && caveman disable codex","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'node:fs';\nconst home = process.env.CAVEMAN_HOME ?? `${process.env.HOME}/.caveman`;\nconst j = JSON.parse(readFileSync(`${home}/integrations/codex.json`, 'utf8'));\nconst bad = j.operations.filter((op) =>\n  typeof op.owned?.root_block !== 'string' || typeof op.owned?.tables_block !== 'string');\nif (bad.length) console.error('journal pre-dates current schema — `caveman enable codex` rewrites it');","typeGuard":"const hasCodexOwnedBlocks = (op) =>\n  typeof op.owned?.root_block === 'string' && typeof op.owned?.tables_block === 'string';","tryCatchPattern":"catch (err) { if (err.message.includes('journal lacks owned blocks')) { /* `caveman enable codex && caveman disable codex` */ } else throw err; }","preventionTips":["Keep ~/.caveman/integrations/*.json untouched by cleanup scripts","Match enable/disable caveman versions; run doctor after upgrades"],"tags":["codex","journal","schema","disable"],"backgroundTag":"schema-validation-failed","analyzedSha":"2f49f0e1a352aa810e70056b7930aeb0b3d219b4","analyzedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}