ruvnet/ruflo · critical · Error
tuneDistillation: source DB checksum changed during tuning —
Error message
tuneDistillation: source DB checksum changed during tuning — refusing to report a result
What it means
tuneDistillation computes a sha256 of the source db before tuning and asserts it is byte-identical afterward. The source db is never opened for writes anywhere in the harness (only read-only temp copies are used), so a checksum change means a broken invariant — typically an external concurrent writer modified the source during the run. To avoid reporting a result built on shifting data, it refuses to return.
Source
Thrown at v3/@claude-flow/cli/src/services/distill-tuning.ts:338
});
const baseline = scoreQuerySet(heldOutBaselineIndex, heldOutQuerySet, topK);
const heldOut: HeldOutScore = {
mrrAt10: winnerFull.mrrAtK,
recallAt10: winnerFull.recallAtK,
queryCount: winnerFull.queryCount,
baselineMrrAt10: baseline.mrrAtK,
baselineRecallAt10: baseline.recallAtK,
};
// Overfit: held-out MRR is more than 20% (relative) worse than the winner's
// own train score. Guard divide-by-zero — a zero train score can't overfit.
const overfit = winner.trainScore > 0 && heldOut.mrrAt10 < winner.trainScore * 0.8;
const sourceChecksumAfter = sha256File(dbPath);
if (sourceChecksumAfter !== sourceChecksumBefore) {
// This must be structurally impossible (dbPath is never opened with a DB
// connection anywhere above) — a mismatch means an invariant was broken.
throw new Error('tuneDistillation: source DB checksum changed during tuning — refusing to report a result');
}
return {
candidates,
winner,
heldOut,
overfit,
provenance: {
gridSize: configs.length,
corpusSize: outerSplit.totalRows,
trainSize: outerSplit.trainRowids.length,
heldOutSize: outerSplit.heldOutRowids.length,
metric: 'mrr@10',
tunedAt: now ?? Date.now(),
sourceDbPath: dbPath,
sourceChecksumSha256: sourceChecksumAfter,
},
};View on GitHub (pinned to 6b01dc5a68)
Solutions
- Quiesce writers against dbPath for the duration of the run, or run tuning against a snapshot copy.
- Copy the source db to an isolated path and pass that copy as dbPath.
- Schedule tuning during idle periods when no memory writes occur.
- If it still fires, treat it as a bug report — the harness guarantees it does not write the source.
Example fix
// before — tuning the live db while agents write to it
await tuneDistillation({ dbPath: liveDbPath, ... }); // may throw 'checksum changed'
// after — run against an isolated copy
const snapshot = path.resolve(tmpDir, 'tune-source.db');
fs.copyFileSync(liveDbPath, snapshot);
await tuneDistillation({ dbPath: snapshot, ... }); Defensive patterns
Strategy: try-catch
Validate before calling
// Prevent by ensuring no concurrent writer touches dbPath during the run.
const snapshot = path.resolve(tmpDir, 'tune-source.db');
fs.copyFileSync(dbPath, snapshot);
await tuneDistillation({ dbPath: snapshot, ... }); Try / catch
try {
return await tuneDistillation({ dbPath, ... });
} catch (e) {
if ((e as Error).message.includes('checksum changed')) {
// retry against an isolated snapshot with writers quiesced
const snap = copyToTemp(dbPath, tmpDir, 'tune-snap');
return await tuneDistillation({ dbPath: snap, ... });
}
throw e;
} Prevention
- Run tuning against a snapshot copy, not the live memory db.
- Quiesce memory writers during tuning windows.
- Never point two tuning jobs at the same db.
- Exclude the db from backup/sync tools that touch it mid-run.
When it happens
Trigger: Another process writes to the same dbPath while tuneDistillation runs (memory system writing new patterns, a separate distillation run, a sync process), or the file was replaced/swapped during the run.
Common situations: Running tuning against the live memory db while agents are actively storing patterns; two tuning jobs pointed at the same db; a backup/sync tool touching the file mid-run.
Related errors
- tuneDistillation: train/held-out partitions are not disjoint
- tuneDistillation: source db not found at ${dbPath}
- tuneDistillation: better-sqlite3 unavailable — cannot run th
- tuneDistillation: empty grid — supply at least one value per
- tuneDistillation: every grid candidate was skipped — see can
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/2bc1cc3b120cd5ab.
Report an issue: GitHub.