ruvnet/ruflo · error · Error
tuneDistillation: empty grid — supply at least one value per
Error message
tuneDistillation: empty grid — supply at least one value per grid axis
What it means
tuneDistillation builds candidate configs as the cartesian product of grid.batchSize x grid.dedupDistance x grid.promoteThreshold (each defaulting to a non-empty array). It throws only if the resulting configs list is empty, which happens only when a caller explicitly passes an empty array for every supplied axis (axes that are omitted fall back to defaults).
Source
Thrown at v3/@claude-flow/cli/src/services/distill-tuning.ts:235
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');
}
// ── Splits + query sets: a dedicated read-only copy, never the source itself ──
const readCopy = copyToTemp(dbPath, tmpDir, 'split-read');
let outerSplit: TimeSplit;
let innerSplit: TimeSplit;
let trainQuerySet: QueryEntry[];
let heldOutQuerySet: QueryEntry[];
let heldOutBaselineIndex: PatternIndexEntry[];
try {
const readDb = new Database(readCopy, { readonly: true });
try {
outerSplit = computeTimeSplit(readDb, trainFraction);
assertDisjoint(outerSplit.trainRowids, outerSplit.heldOutRowids);
innerSplit = computeTimeSplit(readDb, trainFraction, {
extraWhere: `rowid <= ${outerSplit.trainBoundaryRowid}`,
});View on GitHub (pinned to 6b01dc5a68)
Solutions
- Validate the grid before calling: each supplied axis must be a non-empty array of numbers.
- Omit axes you do not want to vary so they fall back to DEFAULT_GRID_BATCH_SIZE / DEFAULT_GRID_DEDUP_DISTANCE / DEFAULT_GRID_PROMOTE_THRESHOLD.
- If computing axes dynamically, default filtered-out arrays to a singleton like [100].
Example fix
// before
tuneDistillation({ dbPath, grid: { batchSize: [], dedupDistance: [], promoteThreshold: [] } }); // throws 'empty grid'
// after
const grid = {
batchSize: computedBatchSizes.length ? computedBatchSizes : [200],
dedupDistance: computedDedups.length ? computedDedups : [0.1],
promoteThreshold: computedThresholds.length ? computedThresholds : [{ min: 0.5 }],
};
await tuneDistillation({ dbPath, grid }); Defensive patterns
Strategy: validation
Validate before calling
function nonEmpty<T>(xs: T[] | undefined, fallback: T[]): T[] {
return xs && xs.length ? xs : fallback;
}
const grid = {
batchSize: nonEmpty(options.grid?.batchSize, [200]),
dedupDistance: nonEmpty(options.grid?.dedupDistance, [0.1]),
promoteThreshold: nonEmpty(options.grid?.promoteThreshold, [defaultThreshold]),
};
await tuneDistillation({ dbPath, grid }); Type guard
function isNonEmptyNumberArray(xs: unknown): xs is number[] {
return Array.isArray(xs) && xs.length > 0 && xs.every((x) => typeof x === 'number');
} Prevention
- Omit grid axes you do not want to vary so defaults apply.
- Validate that each supplied axis is a non-empty numeric array before calling.
- Default programmatically-computed empty arrays to a sensible singleton.
- Unit-test grid construction against the cartesian-product contract.
When it happens
Trigger: Passing grid: { batchSize: [], dedupDistance: [], promoteThreshold: [] }, or supplying one axis as a non-empty array but the other supplied axes as empty arrays such that the product is empty.
Common situations: A config-driven grid where all axes resolve to [] due to a templating bug; misreading the API and passing grid values nested under a wrong key so the destructured axes stay empty; programmatic grid generation that filters every value out.
Related errors
- tuneDistillation: every grid candidate was skipped — see can
- Invalid completion type
- browser/eval: script must not be empty
- Invalid target format: ${target}. Use agent:<id> or human:<i
- Import file must contain a JSON object
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/633770e8435c9cb7.
Report an issue: GitHub.