{"record":{"id":"fe285da33eda9809","repo":"JuliusBrussee/caveman","slug":"agent-servername-mcp-transaction-journal-alr","errorCode":null,"errorMessage":"${agent} ${serverName} MCP transaction journal already exists; refusing overwrite","messagePattern":"(.+?) (.+?) MCP transaction journal already exists; refusing overwrite","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":12917,"sourceCode":"    server_name: serverName,\n    action,\n    config_path: plan.path,\n    marker_path: markerPath,\n    config_before_base64: plan.before?.toString(\"base64\") ?? null,\n    config_before_mode: plan.beforeMode,\n    config_before_sha256: optionalBytesHash(plan.before),\n    config_after_sha256: optionalBytesHash(plan.after),\n    marker_before_base64: markerBefore?.toString(\"base64\") ?? null,\n    marker_before_mode: markerBeforeMode,\n    marker_before_sha256: optionalBytesHash(markerBefore),\n    marker_after_base64: markerAfter?.toString(\"base64\") ?? null,\n    marker_after_sha256: optionalBytesHash(markerAfter),\n  };\n  const locatorPath = `${markerPath}.pending`;\n  const configPendingPath = mcpConfigPendingJournalPath(plan.path);\n  const pendingBytes = Buffer.from(JSON.stringify(journal, null, 2) + \"\\n\");\n  if (fileBytes(locatorPath) !== null || fileBytes(configPendingPath) !== null) {\n    throw new Error(`${agent} ${serverName} MCP transaction journal already exists; refusing overwrite`);\n  }\n  durableCreateFile(locatorPath, pendingBytes);\n  try {\n    durableCreateFile(configPendingPath, pendingBytes);\n  } catch (error) {\n    durableUnlink(locatorPath);\n    throw error;\n  }\n\n  try {\n    if (action === \"install\") {\n      if (plan.changed) durableReplaceFileIfUnchanged(plan.path, plan.before, plan.after, plan.beforeMode);\n      if (!optionalBytesEqual(markerBefore, markerAfter)) durableReplaceFileIfUnchanged(markerPath, markerBefore, markerAfter);\n    } else {\n      if (!optionalBytesEqual(markerBefore, markerAfter)) durableReplaceFileIfUnchanged(markerPath, markerBefore, markerAfter);\n      if (plan.changed) durableReplaceFileIfUnchanged(plan.path, plan.before, plan.after, plan.beforeMode);\n    }\n    if (optionalBytesHash(fileBytes(plan.path)) !== journal.config_after_sha256","sourceCodeStart":12899,"sourceCodeEnd":12935,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L12899-L12935","documentation":"Before starting a transaction the CLI creates two pending-journal copies, and it refuses to overwrite either if they already exist. A leftover journal means a previous transaction never finished being recovered; blindly overwriting it would orphan that transaction's rollback data.","triggerScenarios":"Thrown in transactOwnedMcpConfig when fileBytes(locatorPath) !== null || fileBytes(configPendingPath) !== null — either '<markerPath>.pending' or the config-adjacent pending journal already exists when a new install/uninstall is planned.","commonSituations":"A previous run crashed before recovery could complete and the pending files remain; recovery was skipped because the user answered no/aborted; a previous run threw the 'refusing destructive recovery' errors (500-504) leaving journals in place; stale files from an old CLI version at the same paths.","solutions":["Run any 'caveman mcp' command for the same agent — recovery of the interrupted transaction runs first and cleans the pending copies on success.","If recovery refuses (see the recovery errors), resolve the underlying state (restore before/after content or delete both pending copies) as documented there.","Confirm no other caveman process is mid-transaction before deleting anything; the files may be actively in use.","After clearing stale journals, verify the agent config is in the desired state and re-run the install/uninstall."],"exampleFix":"// before: leftover journal blocks new transaction\nerror: ... MCP transaction journal already exists; refusing overwrite\n// after: trigger recovery (reads and clears pending state)\ncaveman mcp list kilo && caveman mcp install kilo","handlingStrategy":"validation","validationCode":"import { existsSync } from 'node:fs';\nconst stale = [markerPath + '.pending', configPath + '.pending'].filter(existsSync);\nif (stale.length) {\n  console.warn('pending journal exists — run a caveman mcp command to trigger recovery first:', stale.join(', '));\n}","typeGuard":"function noPendingJournal(markerPath: string, configPath: string): boolean {\n  return !existsSync(markerPath + '.pending') && !existsSync(configPath + '.pending');\n}","tryCatchPattern":"try {\n  runMcpCommand();\n} catch (e) {\n  if (String(e.message).includes('transaction journal already exists')) {\n    // any mcp subcommand for the agent runs recovery first and clears the copies on success\n    runAnyMcpCommand(agent); // e.g. list/show\n    runMcpCommand();\n  }\n}","preventionTips":["After a crash or forced abort, run recovery via any caveman mcp command before installing again","Don't bypass recovery prompts; resolving them clears the pending state cleanly","Check for leftover .pending files after abnormal terminations (kill -9, OOM, power loss)","Run caveman mcp operations to completion; avoid closing terminals mid-transaction"],"tags":["mcp","journal","file-already-exists","recovery"],"backgroundTag":"file-already-exists","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"}