{"record":{"id":"3f01147cfa0a6dbd","repo":"JuliusBrussee/caveman","slug":"agent-servername-mcp-transaction-lost-one-du","errorCode":null,"errorMessage":"${agent} ${serverName} MCP transaction lost one durable journal copy; refusing destructive recovery","messagePattern":"(.+?) (.+?) MCP transaction lost one durable journal copy; refusing destructive recovery","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/cli/src/index.ts","lineNumber":12850,"sourceCode":"    if (configPending) durableUnlink(configPendingPath);\n    if (locatorPending) durableUnlink(locatorPath);\n  };\n\n  if (configIsAfter && markerIsAfter) {\n    removePendingCopies();\n    process.stderr.write(`${mark(\"warn\")} finalized interrupted ${agent} ${serverName} MCP transaction\\n`);\n    return \"finalized\";\n  }\n  if (configIsBefore && markerIsBefore) {\n    removePendingCopies();\n    process.stderr.write(`${mark(\"warn\")} discarded uncommitted ${agent} ${serverName} MCP transaction\\n`);\n    return \"discarded\";\n  }\n  if ((!configIsBefore && !configIsAfter) || (!markerIsBefore && !markerIsAfter)) {\n    throw new Error(`${agent} ${serverName} MCP config or ownership journal changed during interrupted transaction; refusing destructive recovery`);\n  }\n  if (!configPending || !locatorPending) {\n    throw new Error(`${agent} ${serverName} MCP transaction lost one durable journal copy; refusing destructive recovery`);\n  }\n\n  if (!configIsBefore) {\n    durableReplaceFileIfUnchanged(configPath, configCurrent, configBefore, journal.config_before_mode);\n  }\n  if (!markerIsBefore) {\n    durableReplaceFileIfUnchanged(markerPath, markerCurrent, markerBefore, journal.marker_before_mode);\n  }\n  if (optionalBytesHash(fileBytes(configPath)) !== journal.config_before_sha256\n    || optionalBytesHash(fileBytes(markerPath)) !== journal.marker_before_sha256) {\n    throw new Error(`${agent} ${serverName} MCP rollback postflight mismatch`);\n  }\n  removePendingCopies();\n  process.stderr.write(`${mark(\"warn\")} rolled back interrupted ${agent} ${serverName} MCP transaction\\n`);\n  return \"rolled-back\";\n}\n\nfunction transactOwnedMcpConfig(","sourceCodeStart":12832,"sourceCodeEnd":12868,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L12832-L12868","documentation":"The transaction protocol requires both durable journal copies (locator '<markerPath>.pending' and the config-adjacent copy) to exist before performing a mid-state rollback. If the current files are in a partial state (neither fully before nor fully after) but one journal copy is missing, the CLI lacks the snapshot data needed to roll back safely and refuses destructive recovery.","triggerScenarios":"recoverOwnedMcpTransaction: thrown when configPending or locatorPending is null (readOwnedMcpPendingJournalAt returned null for the config pending path or the locator path) while the config/marker files are in a mixed before/after state requiring rollback.","commonSituations":"A crash occurred between durableCreateFile(locatorPath) and durableCreateFile(configPendingPath) (the CLI unlinks the locator on that failure, but a crash can leave neither or one); a cleanup script or antivirus removed one .pending file; a partial transaction happened after the config file was moved/renamed so the adjacent pending path no longer resolves.","solutions":["Restore the missing .pending copy: if you have the other copy, duplicate it to the missing path (they must be byte-identical) and re-run to let recovery proceed.","Manually restore the config and marker to a known-good state (from the journal's base64 snapshots or a backup), then delete remaining pending files.","Check for the files under the exact expected paths (journal.config_path-adjacent and '<marker_path>.pending') — a moved config makes the copy appear 'lost'.","Re-run 'caveman mcp install/uninstall' after clearing state to rebuild configs deterministically."],"exampleFix":"// before: only one copy exists\nls marker.mcp.pending        # exists\nls config.json.pending       # missing\n// after: duplicate the surviving copy\ncp marker.mcp.pending config.json.pending","handlingStrategy":"validation","validationCode":"import { existsSync } from 'node:fs';\nconst a = markerPath + '.pending', b = configPath + '.pending';\nif (existsSync(a) !== existsSync(b) && existsSync(a) || existsSync(a) && !existsSync(b)) {\n  console.warn('one durable journal copy is missing — duplicate the surviving copy or restore from backup');\n}","typeGuard":"function hasBothJournalCopies(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('lost one durable journal copy')) {\n    // duplicate the surviving copy to the missing path, or restore before-state manually\n    ensureBothCopies(locatorPath, configPendingPath);\n    runMcpCommand();\n  }\n}","preventionTips":["Keep agent config files at their canonical paths — moving them orphans the config-adjacent journal copy","Exclude caveman state dirs from aggressive cleanup scripts and antivirus scans","After any crash, run recovery before issuing new mcp commands","Monitor disk-full conditions: partial journal creation is a common cause"],"tags":["mcp","recovery","missing-file","data-safety"],"backgroundTag":"file-not-found","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"}