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

  1. Quiesce writers against dbPath for the duration of the run, or run tuning against a snapshot copy.
  2. Copy the source db to an isolated path and pass that copy as dbPath.
  3. Schedule tuning during idle periods when no memory writes occur.
  4. 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

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


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/2bc1cc3b120cd5ab. Report an issue: GitHub.