{"record":{"id":"b654c91becd9950c","repo":"JuliusBrussee/caveman","slug":"pending-label-transaction-is-malformed-refusin","errorCode":null,"errorMessage":"pending ${label} transaction is malformed; refusing recovery","messagePattern":"pending (.+?) transaction is malformed; refusing recovery","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":12362,"sourceCode":"  return agent && serverName ? `${agent} ${serverName} MCP` : \"owned MCP\";\n}\n\nfunction readOwnedMcpPendingJournalAt(\n  path: string,\n  expected: { agent?: \"kilo\" | \"qwen\"; serverName?: string; configPath?: string; locatorPath?: string } = {},\n): ReadOwnedMcpPendingJournal | null {\n  const label = ownedMcpPendingLabel(expected.agent, expected.serverName);\n  let bytes: Buffer;\n  let value: Record<string, unknown>;\n  try {\n    bytes = readFileSync(path);\n    value = JSON.parse(bytes.toString(\"utf8\")) as Record<string, unknown>;\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n    throw new Error(`cannot read pending ${label} transaction: ${(error as Error).message}`);\n  }\n  if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n    throw new Error(`pending ${label} transaction is malformed; refusing recovery`);\n  }\n  if (process.platform !== \"win32\" && (statSync(path).mode & 0o077) !== 0) {\n    throw new Error(`pending ${label} transaction has unsafe permissions; refusing recovery`);\n  }\n  const keys = [\n    \"action\", \"agent\", \"config_after_sha256\", \"config_before_base64\", \"config_before_mode\", \"config_path\",\n    \"config_before_sha256\", \"marker_after_base64\", \"marker_after_sha256\", \"marker_before_base64\", \"marker_before_mode\",\n    \"marker_before_sha256\", \"marker_path\", \"schema_version\", \"server_name\", \"transaction_id\",\n  ].sort();\n  const journalAgent = value.agent;\n  const journalServer = value.server_name;\n  if (Object.keys(value).sort().join(\"\\0\") !== keys.join(\"\\0\")\n    || value.schema_version !== 1\n    || typeof value.transaction_id !== \"string\"\n    || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value.transaction_id)\n    || (journalAgent !== \"kilo\" && journalAgent !== \"qwen\")\n    || typeof journalServer !== \"string\"\n    || !mcpServerToolName(journalServer)","sourceCodeStart":12344,"sourceCodeEnd":12380,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L12344-L12380","documentation":"Thrown during journal recovery when the parsed JSON is not a plain object (null, array, or non-object). The library requires a record-shaped journal before validating its fields, and refuses recovery on anything else to avoid acting on garbage data.","triggerScenarios":"The pending journal file contains 'null', a JSON array like [], or a bare scalar/string instead of the expected {\"schema_version\":..., ...} object.","commonSituations":"A crash or buggy writer emptied the file to 'null'; a script overwrote the journal with an array of entries; manual editing mistakes.","solutions":["Open the .pending journal and confirm it is a single JSON object; fix or delete it.","Delete the malformed journal files and re-run the MCP server transaction to regenerate them.","Check that no concurrent process (editor, sync tool) is rewriting the journal into a different shape."],"exampleFix":"// before (journal content)\n[{ \"schema_version\": 1 }]\n// after\n{ \"schema_version\": 1, \"action\": \"install\", ... }","handlingStrategy":"type-guard","validationCode":"const parsed: unknown = JSON.parse(fs.readFileSync(p, 'utf8'));\nif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { /* abort before recovery */ }","typeGuard":"const isRecord = (v: unknown): v is Record<string, unknown> => v !== null && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"try { recoverPending(); } catch (e) {\n  if (e.message.includes('is malformed; refusing recovery')) { /* delete journal and redo transaction */ }\n}","preventionTips":["Never write 'null' or arrays into journal files; always a single object.","Guard against writers that serialize lists of entries into the journal path.","Validate parsed JSON shape before consuming."],"tags":["journal-validation","json-shape","recovery-refused"],"backgroundTag":"schema-validation-failed","analyzedSha":"5184b3d11ac6a1acb7d44b9bfaa31698157cff97","analyzedAt":"2026-09-06T12:00:26.372Z","contentChangedAt":"2026-09-06T12:00:26.372Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}