{"record":{"id":"4ee7a775110a1fc0","repo":"JuliusBrussee/caveman","slug":"hermes-routing-block-changed-after-enable-refusin","errorCode":null,"errorMessage":"Hermes routing block changed after enable; refusing destructive disable","messagePattern":"Hermes routing block changed after enable; refusing destructive disable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7715,"sourceCode":"    }\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, \"\");\n  }\n  return Buffer.from(text);","sourceCodeStart":7697,"sourceCodeEnd":7733,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/2f49f0e1a352aa810e70056b7930aeb0b3d219b4/packages/cli/src/index.ts#L7697-L7733","documentation":"Thrown while disabling or repairing the Hermes integration when the Hermes config file drifted (hash differs from after_sha256) and the exact journaled route_block string no longer appears in the file. Caveman replaces that block with the original previous_route_lines on disable; if the block was edited or removed after enable, an automatic restore could mangle the config, so it refuses.","triggerScenarios":"Editing or deleting the Caveman routing lines inside the Hermes config after `caveman enable hermes`, then running `caveman disable hermes` or `caveman doctor hermes --fix`.","commonSituations":"Changing the gateway URL line Caveman added; deleting the block manually and forgetting the journal; a config migration tool reformatting the file so the exact string no longer matches.","solutions":["Run `caveman doctor hermes` to confirm drift, then restore the exact owned.route_block string from ~/.caveman/integrations/hermes.json into the config","If you already manually removed the block: finish cleanup by hand and delete the journal file — disable has nothing left to restore","Re-run `caveman enable hermes` to re-journal current state, then disable before further edits"],"exampleFix":"# before: caveman routing block in hermes config was edited\n# >>> caveman:native-routing (modified lines...)\n\n# after: paste back the exact owned.route_block, then run `caveman disable hermes`","handlingStrategy":"try-catch","validationCode":"import { readFileSync } from 'node:fs';\nconst home = process.env.CAVEMAN_HOME ?? `${process.env.HOME}/.caveman`;\nconst j = JSON.parse(readFileSync(`${home}/integrations/hermes.json`, 'utf8'));\nconst op = j.operations.find((o) => o.kind === 'hermes-config');\nconst text = readFileSync(op.file, 'utf8');\nif (!text.includes(op.owned.route_block)) console.error('routing block missing/edited — restore it or clean up manually');","typeGuard":null,"tryCatchPattern":"catch (err) { if (err.message.includes('routing block changed after enable')) { /* restore owned.route_block verbatim, or if you already removed it deliberately, delete the journal and finish cleanup manually */ } else throw err; }","preventionTips":["Do not edit or delete caveman routing lines in the hermes config; disable via the CLI instead","After any manual fiddling, run `caveman enable hermes` to re-sync the journal"],"tags":["hermes","routing","config-drift","disable"],"backgroundTag":"config-drift-detected","analyzedSha":"2f49f0e1a352aa810e70056b7930aeb0b3d219b4","analyzedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}