{"record":{"id":"2be2f5f5e0a5ca3d","repo":"ruvnet/ruflo","slug":"need-at-least-this-numcentroids-training-vector","errorCode":null,"errorMessage":"Need at least ${this.numCentroids} training vectors, got ${vectors.length}","messagePattern":"Need at least (.+?) training vectors, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/integrations/ruvector/quantization.ts","lineNumber":712,"sourceCode":"      throw new Error(\n        `Dimensions (${options.dimensions}) must be divisible by numSubvectors (${options.numSubvectors})`\n      );\n    }\n\n    this.subvectorDim = options.dimensions / options.numSubvectors;\n    this.maxIterations = options.maxIterations ?? 100;\n    this.tolerance = options.tolerance ?? 1e-6;\n    this.rng = createRng(options.seed ?? 42);\n  }\n\n  /**\n   * Trains codebooks from training data using k-means clustering.\n   *\n   * @param vectors - Training vectors\n   */\n  async train(vectors: number[][]): Promise<void> {\n    if (vectors.length < this.numCentroids) {\n      throw new Error(\n        `Need at least ${this.numCentroids} training vectors, got ${vectors.length}`\n      );\n    }\n\n    this.codebooks = [];\n\n    // Train a codebook for each subvector\n    for (let m = 0; m < this.numSubvectors; m++) {\n      // Extract subvectors\n      const subvectors = this.extractSubvectors(vectors, m);\n\n      // Train codebook using k-means\n      const codebook = await this.trainCodebook(subvectors);\n      this.codebooks.push(codebook);\n    }\n\n    this.isTrained = true;\n  }","sourceCodeStart":694,"sourceCodeEnd":730,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/integrations/ruvector/quantization.ts#L694-L730","documentation":"ProductQuantizer.train() (quantization.ts:712) runs k-means per subvector with numCentroids clusters (typically 256 so each code fits one byte). k-means cannot produce K distinct centroids from fewer than K training vectors, so the method throws when vectors.length < numCentroids, reporting both numbers.","triggerScenarios":"Training with K=256 on a dataset of 100 vectors; smoke tests using a handful of fixtures; production corpora smaller than the advertised codebook size.","commonSituations":"See trigger scenarios.","solutions":["Collect at least numCentroids training vectors (256 for byte-sized codes) before calling train()","Lower numCentroids to fit small datasets (e.g. 16 or 32) — compression and recall adjust accordingly","Sample vectors from the real corpus rather than a fixed fixture in tests, or generate >= K synthetic vectors for smoke tests"],"exampleFix":"// before\nconst pq = new ProductQuantizer({ dimensions: 128, numSubvectors: 8, numCentroids: 256 });\nawait pq.train(sample.slice(0, 50)); // 50 < 256 -> throws\n\n// after\nawait pq.train(sample); // ensure sample.length >= 256\n// or for small datasets:\nconst pq = new ProductQuantizer({ dimensions: 128, numSubvectors: 8, numCentroids: 16 });","handlingStrategy":"validation","validationCode":"if (vectors.length < pqNumCentroids) {\n  throw new Error(`PQ training needs >= ${pqNumCentroids} vectors, dataset has ${vectors.length}`);\n}\nawait pq.train(vectors);","typeGuard":"function hasEnoughTrainingData(vectors: number[][], numCentroids: number): boolean {\n  return vectors.length >= numCentroids;\n}","tryCatchPattern":"try {\n  await pq.train(vectors);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('training vectors')) {\n    const small = new ProductQuantizer({ ...opts, numCentroids: 16 });\n    await small.train(vectors);\n  } else throw err;\n}","preventionTips":["Check dataset size against numCentroids (256 for byte codes) before training","Lower numCentroids for small corpora instead of forcing K=256","Guard smoke tests to either generate >= K vectors or use a small K"],"tags":["quantization","product-quantization","k-means","training-data"],"backgroundTag":"insufficient-training-data","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}