{"record":{"id":"015313b7b0fa47ea","repo":"JuliusBrussee/caveman","slug":"field-must-be-base64-or-null","errorCode":null,"errorMessage":"${field} must be base64 or null","messagePattern":"(.+?) must be base64 or null","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":12324,"sourceCode":"}\n\nfunction mcpMarkerBytes(mcp: { command: string; args: string[] }, tool: string, configPath?: string): Buffer {\n  return Buffer.from(JSON.stringify({\n    ...(configPath ? { schema_version: 1 } : {}),\n    tool,\n    command: mcp.command,\n    args: mcp.args,\n    ...(configPath ? { config_path: canonicalMcpConfigPath(configPath) } : {}),\n  }, null, 2) + \"\\n\");\n}\n\nfunction validMcpMarkerBytes(bytes: Buffer, agent: \"kilo\" | \"qwen\", serverName: string): boolean {\n  return parseMcpServerMarkerBytes(agent, serverName, bytes) !== null;\n}\n\nfunction decodePendingBytes(value: unknown, field: string): Buffer | null {\n  if (value === null) return null;\n  if (typeof value !== \"string\") throw new Error(`${field} must be base64 or null`);\n  const bytes = Buffer.from(value, \"base64\");\n  if (bytes.toString(\"base64\") !== value) throw new Error(`${field} is not canonical base64`);\n  return bytes;\n}\n\nfunction validOptionalHash(value: unknown): value is string | null {\n  return value === null || (typeof value === \"string\" && /^sha256:[0-9a-f]{64}$/.test(value));\n}\n\ntype ReadOwnedMcpPendingJournal = {\n  journal: OwnedMcpPendingJournal;\n  configBefore: Buffer | null;\n  markerBefore: Buffer | null;\n  markerAfter: Buffer | null;\n  path: string;\n  bytes: Buffer;\n};\n","sourceCodeStart":12306,"sourceCodeEnd":12342,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L12306-L12342","documentation":"Thrown by decodePendingBytes when a pending-journal field that should hold bytes is neither null nor a base64 string. The journal stores config/marker snapshots as canonical base64; any other JSON type cannot be decoded safely, so recovery refuses to proceed.","triggerScenarios":"Calling the pending-transaction recovery path with a journal JSON file where config_before_base64, marker_before_base64, or marker_after_base64 is a number, object, array, or boolean instead of a base64 string or null.","commonSituations":"Hand-editing a .pending journal file; a third-party tool rewriting the journal with wrong types; schema drift after a version upgrade changed field encodings.","solutions":["Set the offending field to a valid canonical base64 string (Buffer.from(bytes).toString('base64')) or literal null.","Regenerate the journal by re-running the transaction (install/uninstall the MCP server) instead of hand-editing it.","Delete the .pending journal files only if you understand the transaction will not be recovered, then re-apply the config change.","Check the journal against the expected keys/schema_version before recovery."],"exampleFix":"// before\n{ \"config_before_base64\": 123 }\n// after\n{ \"config_before_base64\": \"eyJtY3BTZXJ2ZXJzIjp7fX0=\" }","handlingStrategy":"validation","validationCode":"function isBase64OrNull(v: unknown): boolean {\n  return v === null || (typeof v === 'string' && Buffer.from(v, 'base64').toString('base64') === v);\n}","typeGuard":"const isB64OrNull = (v: unknown): v is string | null => v === null || (typeof v === 'string' && /^[A-Za-z0-9+/]*={0,2}$/.test(v));","tryCatchPattern":"try { journal = readOwnedMcpPendingJournal(); } catch (e) { console.error('Pending journal unusable, skipping recovery:', e.message); }","preventionTips":["Always encode journal byte fields with Buffer.from(bytes).toString('base64').","Never hand-edit .pending journal files.","Validate journal JSON against the schema before attempting recovery."],"tags":["base64","journal-validation","type-mismatch"],"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-14T05:17:10.506Z"}