{"record":{"id":"c1ed88c563807d45","repo":"JuliusBrussee/caveman","slug":"pending-label-transaction-is-malformed-erro","errorCode":null,"errorMessage":"pending ${label} transaction is malformed: ${(error as Error).message}","messagePattern":"pending (.+?) transaction is malformed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":12793,"sourceCode":"      || optionalBytesHash(markerAfter) !== value.marker_after_sha256\n      || (markerBefore !== null && !validMcpMarkerBytes(markerBefore, journalAgent, journalServer))\n      || (markerAfter !== null && !validMcpMarkerBytes(markerAfter, journalAgent, journalServer))\n      || (markerBefore !== null && parseMcpServerMarkerBytes(journalAgent, journalServer, markerBefore)?.config_path !== undefined\n        && parseMcpServerMarkerBytes(journalAgent, journalServer, markerBefore)?.config_path !== value.config_path)\n      || (markerAfter !== null && parseMcpServerMarkerBytes(journalAgent, journalServer, markerAfter)?.config_path !== value.config_path)\n      || (value.action === \"install\") !== (markerAfter !== null)) {\n      throw new Error(\"journal bytes do not match declared transaction state\");\n    }\n    return {\n      journal: value as OwnedMcpPendingJournal,\n      configBefore,\n      markerBefore,\n      markerAfter,\n      path,\n      bytes,\n    };\n  } catch (error) {\n    throw new Error(`pending ${label} transaction is malformed: ${(error as Error).message}`);\n  }\n}\n\nfunction readOwnedMcpPendingLocator(agent: \"kilo\" | \"qwen\", serverName: string): ReadOwnedMcpPendingJournal | null {\n  const path = canonicalMcpConfigPath(mcpPendingJournalPath(agent, serverName));\n  return readOwnedMcpPendingJournalAt(path, { agent, serverName, locatorPath: path });\n}\n\nfunction readOwnedMcpConfigPending(configPath: string): ReadOwnedMcpPendingJournal | null {\n  const canonicalPath = canonicalMcpConfigPath(configPath);\n  return readOwnedMcpPendingJournalAt(mcpConfigPendingJournalPath(canonicalPath), { configPath: canonicalPath });\n}\n\ntype OwnedMcpRecovery = \"none\" | \"discarded\" | \"finalized\" | \"rolled-back\";\n\nfunction recoverOwnedMcpTransaction(pending: ReadOwnedMcpPendingJournal | null): OwnedMcpRecovery {\n  if (!pending) return \"none\";\n  const { journal, configBefore, markerBefore } = pending;","sourceCodeStart":12775,"sourceCodeEnd":12811,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L12775-L12811","documentation":"readOwnedMcpPendingJournalAt validates a saved MCP transaction journal (schema fields, hashes, and the base64-encoded config/marker snapshots). After the structural checks pass, it decodes the snapshot bytes and verifies their SHA-256 hashes against the journal's declared hashes and marker consistency. If that inner verification throws, the function rethrows as 'pending <label> transaction is malformed: <reason>', meaning the journal file is internally inconsistent and safe recovery is refused.","triggerScenarios":"Called during MCP transaction recovery (recoverOwnedMcpTransaction via readOwnedMcpConfigPending or readOwnedMcpPendingLocator). Thrown when decodePendingBytes fails on config_before_base64/marker_before_base64/marker_after_base64, when the decoded bytes' SHA-256 does not match the journal's *_sha256 fields, when marker bytes fail validMcpMarkerBytes, when marker config_path disagrees with journal.config_path, or when action ('install') does not correspond to a non-null marker_after snapshot.","commonSituations":"A .pending journal file was hand-edited or truncated mid-write; a user copied a journal between machines with different line endings; an interrupted durableCreateFile left a partial file; an old-schema journal from a previous CLI version sits in the marker path.","solutions":["Delete both pending journal copies (<markerPath>.pending and the config-adjacent pending journal) so recovery falls back to 'none' and the CLI can re-plan the transaction.","Restore the config file and ownership marker from backup or re-run 'caveman mcp install/uninstall <agent>' to rebuild state from scratch.","If you must inspect, decode the *_base64 fields and recompute SHA-256 to find which snapshot is corrupt; do not edit the journal in place.","Reinstall or upgrade the CLI to ensure journal schema version compatibility (schema_version must be 1)."],"exampleFix":"// before: hand-edited journal with mismatched hash\n{ \"config_before_base64\": \"e30=\", \"config_before_sha256\": \"abc...\" }\n// after: remove stale pending journals and re-run the command\nrm ~/.caveman/markers/kilo/<server>.mcp.pending <config-dir>/<config>.pending\ncaveman mcp install kilo","handlingStrategy":"validation","validationCode":"import { readFileSync, existsSync } from 'node:fs';\nconst pendings = [markerPath + '.pending', configPath + '.pending'];\nif (pendings.some(p => existsSync(p))) {\n  const j = JSON.parse(readFileSync(pendings[0], 'utf8'));\n  if (j.schema_version !== 1) throw new Error('stale/foreign journal — run caveman mcp recovery or delete pending copies');\n}","typeGuard":"function isWellFormedJournal(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n    && (v as any).schema_version === 1\n    && typeof (v as any).transaction_id === 'string';\n}","tryCatchPattern":"try {\n  await runMcpCommand();\n} catch (e) {\n  if (String(e.message).includes('transaction is malformed')) {\n    for (const p of [markerPath + '.pending', configPath + '.pending']) rmSafe(p);\n    await runMcpCommand(); // replans from scratch\n  }\n}","preventionTips":["Never hand-edit .pending journal files or the ownership marker","Let the CLI finish transactions; don't kill it mid 'caveman mcp' commands","Exclude ~/.caveman and agent config dirs from sync/backup tools that rewrite files","After a crash, run a benign 'caveman mcp' command to trigger recovery before editing configs"],"tags":["mcp","journal-corruption","integrity-check","recovery"],"backgroundTag":"checksum-mismatch","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"}