ruvnet/ruflo · error · Error

tuneDistillation: better-sqlite3 unavailable — cannot run th

Error message

tuneDistillation: better-sqlite3 unavailable — cannot run the tuning harness

What it means

tuneDistillation needs better-sqlite3 to open read-only copies and run the candidate harness; loadBetterSqlite3() returns null when the native module cannot be loaded. Because tuning requires real sqlite I/O, an absent native binding is unrecoverable for this function (unlike the sql.js WASM path used elsewhere for persistence).

Source

Thrown at v3/@claude-flow/cli/src/services/distill-tuning.ts:218

  const {
    dbPath,
    grid = {},
    namespaces,
    trainFraction = DEFAULT_TRAIN_FRACTION,
    queryNamespaces = DEFAULT_QUERY_NAMESPACES,
    topK = DEFAULT_TOP_K,
    now,
    tmpDir = os.tmpdir(),
    verbose = false,
  } = options;

  if (!dbPath || !fs.existsSync(dbPath)) {
    throw new Error(`tuneDistillation: source db not found at ${dbPath}`);
  }

  const Database = await loadBetterSqlite3();
  if (!Database) {
    throw new Error('tuneDistillation: better-sqlite3 unavailable — cannot run the tuning harness');
  }

  const sourceChecksumBefore = sha256File(dbPath);

  const batchSizes = grid.batchSize ?? DEFAULT_GRID_BATCH_SIZE;
  const dedupDistances = grid.dedupDistance ?? DEFAULT_GRID_DEDUP_DISTANCE;
  const promoteThresholds = grid.promoteThreshold ?? DEFAULT_GRID_PROMOTE_THRESHOLD;
  const configs: TuningConfig[] = [];
  for (const batchSize of batchSizes) {
    for (const dedupDistance of dedupDistances) {
      for (const promoteThreshold of promoteThresholds) {
        configs.push({ batchSize, dedupDistance, promoteThreshold });
      }
    }
  }
  if (configs.length === 0) {
    throw new Error('tuneDistillation: empty grid — supply at least one value per grid axis');
  }

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Install/rebuild the native module: npm install better-sqlite3 (or npm rebuild better-sqlite3).
  2. Ensure build tools are present on the host (python3, make, g++) so node-gyp can compile when no prebuilt binary matches.
  3. On Electron, rebuild for the target ABI with electron-rebuild.
  4. If the platform cannot provide better-sqlite3, do not call tuneDistillation — gate on a capability check first.

Example fix

// before
await tuneDistillation({ dbPath, ... }); // throws 'better-sqlite3 unavailable'

// after — gate the call
const Database = await loadBetterSqlite3();
if (!Database) {
  console.warn('better-sqlite3 missing; skipping distillation tuning');
} else {
  await tuneDistillation({ dbPath, ... });
}
Defensive patterns

Strategy: validation

Validate before calling

const Database = await loadBetterSqlite3();
if (!Database) {
  console.warn('better-sqlite3 unavailable; skipping tuneDistillation');
} else {
  await tuneDistillation({ dbPath, ... });
}

Try / catch

try {
  await tuneDistillation({ dbPath, ... });
} catch (e) {
  if ((e as Error).message.includes('better-sqlite3 unavailable')) {
    // install/rebuild the native module, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: better-sqlite3 is an optionalDependency that failed to install or load: missing native build tools at npm install, an ABI/arch mismatch (e.g. under a different Node/Electron major), the package was pruned by --omit=optional, or a corrupted prebuilt binary.

Common situations: CI image without build-essential / python where prebuilt binaries are unavailable for the platform; upgrading Node.js major without reinstalling native deps; running inside Electron where the ABI does not match the installed Node binding; Alpine/musl where prebuilt glibc binaries fail.

Related errors


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