JuliusBrussee/caveman · error · Error
must be base64 or null
Error message
${field} must be base64 or null What it means
decodePendingBytes validates a field of a pending MCP transaction journal. The value must be either JSON null or a base64 string; any other JSON type (number, boolean, object, array) is rejected. This enforces the journal's schema before bytes are decoded for integrity checks.
Solutions
- Restore the journal field to a valid base64 string or explicit null
- Delete the pending transaction journal and re-run the MCP operation from scratch
- Regenerate the journal by re-running the interrupted command instead of hand-editing
Example fix
// before
{ "config_before_base64": 12345 }
// after
{ "config_before_base64": "eyJhIjoxfQ==" } Defensive patterns
Strategy: type-guard
Validate before calling
function isBase64OrNull(v: unknown): boolean {
return v === null || (typeof v === 'string' && Buffer.from(v, 'base64').toString('base64') === v);
} Type guard
function isDecodablePendingValue(v: unknown): v is string | null {
return v === null || typeof v === 'string';
} Try / catch
try {
decodePendingBytes(journal.config_before_base64, 'config_before_base64');
} catch (e) {
if (e instanceof Error && e.message.includes('must be base64 or null')) {
// treat journal as invalid: discard or regenerate
} else throw e;
} Prevention
- Never hand-edit journal base64 fields to non-string JSON types
- Validate journal JSON types before writing it back
- Use a schema validator on the journal after any manual modification
- Regenerate journals via the CLI rather than crafting them manually
When it happens
Trigger: A hand-edited or partially written pending-transaction journal file contains a non-string, non-null value for config_before_base64, marker_before_base64, or marker_after_base64.
Common situations: Manually editing the journal file to repair a stuck transaction; a tool corrupting the JSON types; an older/newer schema version writing differently-typed fields.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- is not canonical base64
- pending transaction is malformed; refusing recovery
- : MCP ownership journal failed; native config may already…
- MCP transaction journal already exists; refusing overwrite
- MCP transaction journals disagree; refusing recovery
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/015313b7b0fa47ea.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:12695
}
function mcpMarkerBytes(mcp: { command: string; args: string[] }, tool: string, configPath?: string): Buffer {
return Buffer.from(JSON.stringify({
...(configPath ? { schema_version: 1 } : {}),
tool,
command: mcp.command,
args: mcp.args,
...(configPath ? { config_path: canonicalMcpConfigPath(configPath) } : {}),
}, null, 2) + "\n");
}
function validMcpMarkerBytes(bytes: Buffer, agent: "kilo" | "qwen", serverName: string): boolean {
return parseMcpServerMarkerBytes(agent, serverName, bytes) !== null;
}
function decodePendingBytes(value: unknown, field: string): Buffer | null {
if (value === null) return null;
if (typeof value !== "string") throw new Error(`${field} must be base64 or null`);
const bytes = Buffer.from(value, "base64");
if (bytes.toString("base64") !== value) throw new Error(`${field} is not canonical base64`);
return bytes;
}
function validOptionalHash(value: unknown): value is string | null {
return value === null || (typeof value === "string" && /^sha256:[0-9a-f]{64}$/.test(value));
}
type ReadOwnedMcpPendingJournal = {
journal: OwnedMcpPendingJournal;
configBefore: Buffer | null;
markerBefore: Buffer | null;
markerAfter: Buffer | null;
path: string;
bytes: Buffer;
};
View on GitHub (pinned to 3ee70a1026)