{"record":{"id":"633770e8435c9cb7","repo":"ruvnet/ruflo","slug":"tunedistillation-empty-grid-supply-at-least-one","errorCode":null,"errorMessage":"tuneDistillation: empty grid — supply at least one value per grid axis","messagePattern":"tuneDistillation: empty grid — supply at least one value per grid axis","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/distill-tuning.ts","lineNumber":235,"sourceCode":"  if (!Database) {\n    throw new Error('tuneDistillation: better-sqlite3 unavailable — cannot run the tuning harness');\n  }\n\n  const sourceChecksumBefore = sha256File(dbPath);\n\n  const batchSizes = grid.batchSize ?? DEFAULT_GRID_BATCH_SIZE;\n  const dedupDistances = grid.dedupDistance ?? DEFAULT_GRID_DEDUP_DISTANCE;\n  const promoteThresholds = grid.promoteThreshold ?? DEFAULT_GRID_PROMOTE_THRESHOLD;\n  const configs: TuningConfig[] = [];\n  for (const batchSize of batchSizes) {\n    for (const dedupDistance of dedupDistances) {\n      for (const promoteThreshold of promoteThresholds) {\n        configs.push({ batchSize, dedupDistance, promoteThreshold });\n      }\n    }\n  }\n  if (configs.length === 0) {\n    throw new Error('tuneDistillation: empty grid — supply at least one value per grid axis');\n  }\n\n  // ── Splits + query sets: a dedicated read-only copy, never the source itself ──\n  const readCopy = copyToTemp(dbPath, tmpDir, 'split-read');\n  let outerSplit: TimeSplit;\n  let innerSplit: TimeSplit;\n  let trainQuerySet: QueryEntry[];\n  let heldOutQuerySet: QueryEntry[];\n  let heldOutBaselineIndex: PatternIndexEntry[];\n  try {\n    const readDb = new Database(readCopy, { readonly: true });\n    try {\n      outerSplit = computeTimeSplit(readDb, trainFraction);\n      assertDisjoint(outerSplit.trainRowids, outerSplit.heldOutRowids);\n\n      innerSplit = computeTimeSplit(readDb, trainFraction, {\n        extraWhere: `rowid <= ${outerSplit.trainBoundaryRowid}`,\n      });","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/distill-tuning.ts#L217-L253","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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]."],"exampleFix":"// before\ntuneDistillation({ dbPath, grid: { batchSize: [], dedupDistance: [], promoteThreshold: [] } }); // throws 'empty grid'\n\n// after\nconst grid = {\n  batchSize: computedBatchSizes.length ? computedBatchSizes : [200],\n  dedupDistance: computedDedups.length ? computedDedups : [0.1],\n  promoteThreshold: computedThresholds.length ? computedThresholds : [{ min: 0.5 }],\n};\nawait tuneDistillation({ dbPath, grid });","handlingStrategy":"validation","validationCode":"function nonEmpty<T>(xs: T[] | undefined, fallback: T[]): T[] {\n  return xs && xs.length ? xs : fallback;\n}\nconst grid = {\n  batchSize: nonEmpty(options.grid?.batchSize, [200]),\n  dedupDistance: nonEmpty(options.grid?.dedupDistance, [0.1]),\n  promoteThreshold: nonEmpty(options.grid?.promoteThreshold, [defaultThreshold]),\n};\nawait tuneDistillation({ dbPath, grid });","typeGuard":"function isNonEmptyNumberArray(xs: unknown): xs is number[] {\n  return Array.isArray(xs) && xs.length > 0 && xs.every((x) => typeof x === 'number');\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["distillation","tuning","validation","typescript"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}