{"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":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":8422,"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":8404,"sourceCodeEnd":8440,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L8404-L8440","documentation":"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.","triggerScenarios":"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.","commonSituations":"CLI upgrade changed the Codex journal schema; state file truncated; user deleted journal files while keeping the modified Codex config.","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."],"exampleFix":"// missing journal state\n{ \"owned\": { \"tables_block\": \"...\" } }  // root_block absent\n// after re-enable\n{ \"owned\": { \"root_block\": \"# caveman root ...\", \"tables_block\": \"# caveman tables ...\" } }","handlingStrategy":"validation","validationCode":"const owned = journal.owned ?? {};\nif (typeof owned.root_block !== 'string' || typeof owned.tables_block !== 'string') {\n  throw new Error('Codex journal incomplete; re-enable before disabling');\n}","typeGuard":"const hasCodexBlocks = (owned) =>\n  typeof owned?.root_block === 'string' && owned.root_block.length > 0 &&\n  typeof owned?.tables_block === 'string' && owned.tables_block.length > 0;","tryCatchPattern":"try {\n  await caveman.disable(['codex']);\n} catch (err) {\n  if (err.message.includes('journal lacks owned blocks')) {\n    console.error('Journal missing; manually strip Caveman blocks from the Codex TOML config.');\n  } else throw err;\n}","preventionTips":["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."],"tags":["journal","codex","disable-safety"],"backgroundTag":"missing-required-config-field","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}