JuliusBrussee/caveman · error · Error
MCP transaction postflight mismatch
Error message
${agent} ${serverName} MCP transaction postflight mismatch What it means
After applying the planned config and marker writes, the CLI re-hashes both files and verifies they match the journal's recorded after-hashes (config_after_sha256 / marker_after_sha256). If they don't, the writes were skipped (durableReplaceFileIfUnchanged detected concurrent modification) or something rewrote the files mid-transaction; the transaction aborts and safe rollback is attempted.
Solutions
- Re-run the command: the built-in rollback runs first, and once the competing writer is gone the retried transaction should commit cleanly.
- Close/pause the agent and any config-sync tools while running 'caveman mcp install/uninstall'.
- Check write permissions and free disk space on the config directory; fix and retry.
- If rollback was also blocked, the CLI reports 'failed and safe recovery was blocked' — resolve recovery per the recovery-error guidance, then retry.
Example fix
// before: agent rewrites config mid-install caveman mcp install kilo # postflight mismatch, rolled back // after: stop the agent, then install pkill -f claude && caveman mcp install kilo
Defensive patterns
Strategy: try-catch
Validate before calling
import { accessSync, W_OK } from 'node:fs';
try { accessSync(configPath, W_OK); }
catch { console.error('config not writable — the transaction postflight will fail'); }
// also ensure the agent process is stopped before installing Type guard
function agentConfigIsQuiescent(configPath: string, quietMs = 1000): Promise<boolean> {
const s1 = statSync(configPath).mtimeMs;
return new Promise(r => setTimeout(() => r(statSync(configPath).mtimeMs === s1), quietMs));
} Try / catch
try {
runMcpCommand();
} catch (e) {
if (String(e.message).includes('transaction failed and rolled back')) {
stopAgentProcesses();
runMcpCommand(); // retry after rollback
}
} Prevention
- Stop the target agent before running caveman mcp install/uninstall
- Disable config auto-save/watchers on agent config files during transactions
- Verify write permissions and free disk space in the config directory
- Retry the command after any postflight mismatch — rollback restores prior state safely
When it happens
Trigger: Thrown in transactOwnedMcpConfig's commit phase when optionalBytesHash(fileBytes(plan.path)) !== journal.config_after_sha256 || the marker hash !== marker_after_sha256 — i.e. a conditional replace was skipped or a concurrent writer changed the files between write and verification.
Common situations: The agent application (or a config watcher) rewrote its MCP config during the transaction; a second caveman process raced the same transaction; read-only or full disk silently degraded the write; the plan's after-state was computed from stale input JSON.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- MCP rollback postflight mismatch
- MCP changes require the ownership transaction
- MCP config target changed while acquiring lock; refusing…
- MCP transaction failed and rolled back
- MCP transaction failed and safe recovery was blocked: …
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/38db2d307cf94dee.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:12937
durableCreateFile(locatorPath, pendingBytes);
try {
durableCreateFile(configPendingPath, pendingBytes);
} catch (error) {
durableUnlink(locatorPath);
throw error;
}
try {
if (action === "install") {
if (plan.changed) durableReplaceFileIfUnchanged(plan.path, plan.before, plan.after, plan.beforeMode);
if (!optionalBytesEqual(markerBefore, markerAfter)) durableReplaceFileIfUnchanged(markerPath, markerBefore, markerAfter);
} else {
if (!optionalBytesEqual(markerBefore, markerAfter)) durableReplaceFileIfUnchanged(markerPath, markerBefore, markerAfter);
if (plan.changed) durableReplaceFileIfUnchanged(plan.path, plan.before, plan.after, plan.beforeMode);
}
if (optionalBytesHash(fileBytes(plan.path)) !== journal.config_after_sha256
|| optionalBytesHash(fileBytes(markerPath)) !== journal.marker_after_sha256) {
throw new Error(`${agent} ${serverName} MCP transaction postflight mismatch`);
}
durableUnlink(configPendingPath);
durableUnlink(locatorPath);
return true;
} catch (error) {
try {
const recovery = recoverOwnedMcpTransaction(readOwnedMcpConfigPending(plan.path) ?? readOwnedMcpPendingLocator(agent, serverName));
if (recovery === "finalized") return true;
} catch (recoveryError) {
throw new Error(`${agent} ${serverName} MCP transaction failed and safe recovery was blocked: ${(recoveryError as Error).message}; original error: ${(error as Error).message}`);
}
throw new Error(`${agent} ${serverName} MCP transaction failed and rolled back: ${(error as Error).message}`);
}
}
function withOwnedMcpTransactionLock<T>(
agent: "kilo" | "qwen",
serverName: string,View on GitHub (pinned to 3ee70a1026)