ruvnet/ruflo · error

Need at least ${this.numCentroids} training vectors, got ${v

Error message

Need at least ${this.numCentroids} training vectors, got ${vectors.length}

What it means

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.

Source

Thrown at v3/@claude-flow/plugins/src/integrations/ruvector/quantization.ts:712

      throw new Error(
        `Dimensions (${options.dimensions}) must be divisible by numSubvectors (${options.numSubvectors})`
      );
    }

    this.subvectorDim = options.dimensions / options.numSubvectors;
    this.maxIterations = options.maxIterations ?? 100;
    this.tolerance = options.tolerance ?? 1e-6;
    this.rng = createRng(options.seed ?? 42);
  }

  /**
   * Trains codebooks from training data using k-means clustering.
   *
   * @param vectors - Training vectors
   */
  async train(vectors: number[][]): Promise<void> {
    if (vectors.length < this.numCentroids) {
      throw new Error(
        `Need at least ${this.numCentroids} training vectors, got ${vectors.length}`
      );
    }

    this.codebooks = [];

    // Train a codebook for each subvector
    for (let m = 0; m < this.numSubvectors; m++) {
      // Extract subvectors
      const subvectors = this.extractSubvectors(vectors, m);

      // Train codebook using k-means
      const codebook = await this.trainCodebook(subvectors);
      this.codebooks.push(codebook);
    }

    this.isTrained = true;
  }

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Collect at least numCentroids training vectors (256 for byte-sized codes) before calling train()
  2. Lower numCentroids to fit small datasets (e.g. 16 or 32) — compression and recall adjust accordingly
  3. Sample vectors from the real corpus rather than a fixed fixture in tests, or generate >= K synthetic vectors for smoke tests

Example fix

// before
const pq = new ProductQuantizer({ dimensions: 128, numSubvectors: 8, numCentroids: 256 });
await pq.train(sample.slice(0, 50)); // 50 < 256 -> throws

// after
await pq.train(sample); // ensure sample.length >= 256
// or for small datasets:
const pq = new ProductQuantizer({ dimensions: 128, numSubvectors: 8, numCentroids: 16 });
Defensive patterns

Strategy: validation

Validate before calling

if (vectors.length < pqNumCentroids) {
  throw new Error(`PQ training needs >= ${pqNumCentroids} vectors, dataset has ${vectors.length}`);
}
await pq.train(vectors);

Type guard

function hasEnoughTrainingData(vectors: number[][], numCentroids: number): boolean {
  return vectors.length >= numCentroids;
}

Try / catch

try {
  await pq.train(vectors);
} catch (err) {
  if (err instanceof Error && err.message.includes('training vectors')) {
    const small = new ProductQuantizer({ ...opts, numCentroids: 16 });
    await small.train(vectors);
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: See trigger scenarios.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/2be2f5f5e0a5ca3d. Report an issue: GitHub.