{"record":{"id":"374e9daac13d63c3","repo":"JuliusBrussee/caveman","slug":"cannot-read-pending-label-transaction-error","errorCode":null,"errorMessage":"cannot read pending ${label} transaction: ${(error as Error).message}","messagePattern":"cannot read pending (.+?) transaction: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":12359,"sourceCode":"};\n\nfunction ownedMcpPendingLabel(agent?: string, serverName?: string): string {\n  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)","sourceCodeStart":12341,"sourceCodeEnd":12377,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L12341-L12377","documentation":"Wraps any non-ENOENT error from reading or parsing the pending transaction journal file (readFileSync/JSON.parse) during recovery. It means the journal exists but could not be read as JSON — for example a permissions or I/O error — and recovery is aborted so it cannot act on incomplete transaction state.","triggerScenarios":"readFileSync on the journal path fails with EACCES, EISDIR, or another errno other than ENOENT; JSON.parse fails because the file content is invalid JSON (both produce this wrapped error; ENOENT alone returns null).","commonSituations":"Journal file truncated or corrupted by a crash mid-write; file owned by root while recovery runs as the user; path is actually a directory; disk I/O errors.","solutions":["Inspect the wrapped ${(error as Error).message} in the thrown error to identify the underlying errno or JSON parse failure.","Fix file permissions/ownership on the .pending journal file (chmod 600, chown to the running user).","If the file is corrupted, remove it (and the paired config/locator pending journals) and re-run the MCP server install to recreate clean state.","Run the recovery under the same user account that created the transaction."],"exampleFix":"// before\nsudo kilo mcp recover\n// after\nchown $USER ~/.kilo/mcp/*.pending && chmod 600 ~/.kilo/mcp/*.pending && kilo mcp recover","handlingStrategy":"try-catch","validationCode":"try { fs.accessSync(journalPath, fs.constants.R_OK); } catch { /* cannot read: fix perms before recovery */ }","typeGuard":null,"tryCatchPattern":"try { recoverPending(); } catch (e) {\n  if (e.message.startsWith('cannot read pending')) {\n    // inspect cause after the colon; fix perms or delete corrupt journal\n  }\n}","preventionTips":["Create journals with mode 0600 under the same user that runs recovery.","Protect journals from concurrent writers (single CLI instance).","Treat unexpected non-ENOENT read errors as corruption signals and recreate state."],"tags":["file-read","json-parse","journal-recovery"],"backgroundTag":"file-read-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"}