{"record":{"id":"c2dcaeb2f7d028e6","repo":"JuliusBrussee/caveman","slug":"hermes-integration-journal-lacks-owned-routing-blo","errorCode":null,"errorMessage":"Hermes integration journal lacks owned routing block","messagePattern":"Hermes integration journal lacks owned routing block","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":8408,"sourceCode":"    if (!text.includes(routeBlock) || !text.includes(readBlock)) {\n      throw new Error(\"Aider Caveman config block changed after enable; refusing destructive disable\");\n    }\n    text = text.replace(routeBlock, typeof previousRouteLine === \"string\" ? previousRouteLine : \"\");\n    text = text.replace(readBlock, \"\");\n    return Buffer.from(text.replace(/\\n{3,}/g, \"\\n\\n\"));\n  }\n  if (operation.kind === \"aider-core\") {\n    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);\n  }\n  if (operation.kind === \"hermes-plugin-manifest\" || operation.kind === \"hermes-plugin-init\") {\n    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);\n  }\n  if (operation.kind === \"hermes-config\") {\n    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, \"\");","sourceCodeStart":8390,"sourceCodeEnd":8426,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L8390-L8426","documentation":"Hermes config rollback needs the journaled route_block string and the previous_route_lines array (the pre-enable content to restore). If either is missing or of the wrong type in operation.owned, the rollback cannot proceed and the tool throws. This indicates corrupt/incomplete journal state rather than a user config problem.","triggerScenarios":"Running disable with operation.kind === \"hermes-config\" when operation.owned.route_block is not a string or operation.owned.previous_route_lines is not an array — journal written by an older CLI version, truncated, or hand-edited.","commonSituations":"CLI version skew between enable (old journal schema) and disable (new schema); state file corrupted; partially synced dotfile state across machines.","solutions":["Manually remove the Caveman routing block from the Hermes config and restore prior routing lines by hand.","Re-run enable with the current CLI version to regenerate a complete journal, then disable.","Upgrade/downgrade the CLI so both operations share the same journal schema."],"exampleFix":"// corrupt journal\n{ \"owned\": { \"route_block\": 42 } }\n// after re-enable\n{ \"owned\": { \"route_block\": \"# caveman-route ...\", \"previous_route_lines\": [\"model: x\"] } }","handlingStrategy":"validation","validationCode":"const owned = journal.owned ?? {};\nif (typeof owned.route_block !== 'string' || !Array.isArray(owned.previous_route_lines)) {\n  throw new Error('Hermes journal incomplete; re-enable before disabling');\n}","typeGuard":"const hasHermesRouteJournal = (owned) =>\n  typeof owned?.route_block === 'string' && owned.route_block.length > 0 &&\n  Array.isArray(owned?.previous_route_lines);","tryCatchPattern":"try {\n  await caveman.disable(['hermes']);\n} catch (err) {\n  if (err.message.includes('lacks owned routing block')) {\n    console.error('Journal corrupt; strip the Caveman routing block from the Hermes config manually.');\n  } else throw err;\n}","preventionTips":["Don't edit the Caveman state/journal files.","Run enable and disable with the same CLI version.","Re-enable to regenerate the journal if you suspect schema drift.","Back up the Hermes config before enable so manual restore is possible."],"tags":["journal","hermes","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"}