ruvnet/ruflo · error
ProductQuantizer must be trained before encoding
Error message
ProductQuantizer must be trained before encoding
What it means
ProductQuantizer.encode() (quantization.ts:869) maps each subvector to its nearest codebook centroid, which is impossible before train() has built the codebooks. The isTrained flag is protected, set only after successful training, and encode() refuses to run while it is false, throwing this error to prevent encoding against undefined centroids.
Source
Thrown at v3/@claude-flow/plugins/src/integrations/ruvector/quantization.ts:869
const dist = squaredEuclideanDistance(subvector, centroids[i]);
if (dist < minDist) {
minDist = dist;
minIdx = i;
}
}
return minIdx;
}
/**
* Encodes vectors to PQ codes.
*
* @param vectors - Input vectors
* @returns PQ codes (one byte per subvector, assuming K=256)
*/
encode(vectors: number[][]): Uint8Array[] {
if (!this.isTrained) {
throw new Error('ProductQuantizer must be trained before encoding');
}
return vectors.map((vec) => {
const codes = new Uint8Array(this.numSubvectors);
for (let m = 0; m < this.numSubvectors; m++) {
const start = m * this.subvectorDim;
const subvector = vec.slice(start, start + this.subvectorDim);
codes[m] = this.findNearestCentroid(subvector, this.codebooks[m].centroids);
}
return codes;
});
}
/**
* Implements IQuantizer interface - encodes vectors.
*/View on GitHub (pinned to fa13ee4ad6)
Solutions
- Await train(trainingVectors) to completion before any encode() call
- Wrap train() so failures abort the pipeline instead of falling through to encode
- When restoring from persistence, use the deserialize path that rebuilds codebooks and trained state rather than constructing a new instance
- Order pipeline stages explicitly: train -> encode -> store
Example fix
// before
const pq = new ProductQuantizer({ dimensions: 128, numSubvectors: 8, numCentroids: 256 });
const codes = pq.encode(vectors); // throws: not trained
// after
await pq.train(trainingVectors); // must succeed first
const codes = pq.encode(vectors); Defensive patterns
Strategy: try-catch
Validate before calling
// isTrained is protected; track training state at the call site.
let trained = false;
await pq.train(trainingVectors);
trained = true;
if (!trained) throw new Error('Refusing to encode: quantizer not trained');
const codes = pq.encode(vectors); Try / catch
try {
codes = pq.encode(vectors);
} catch (err) {
if (err instanceof Error && err.message === 'ProductQuantizer must be trained before encoding') {
await pq.train(trainingVectors); // train once, then retry
codes = pq.encode(vectors);
} else throw err;
} Prevention
- Always await train() before encode() and abort the pipeline if train() throws
- Avoid blanket catch/continue around training steps that masks an untrained state
- When restoring quantizers from storage, use the deserialization path that rebuilds codebooks instead of new ProductQuantizer()
When it happens
Trigger: Calling encode(vectors) on a freshly constructed ProductQuantizer; train() threw midway (e.g. insufficient data) and the error was swallowed, leaving the quantizer untrained; deserializing a quantizer without restoring codebooks before encoding; separate encode/decode services where the encoder node never ran training.
Common situations: Skipping the training step in quick prototypes; orchestration bugs that run the encode stage before the train stage; exceptions from train() caught too broadly so the pipeline continues.
Related errors
- Dimensions (${options.dimensions}) must be divisible by numS
- Need at least ${this.numCentroids} training vectors, got ${v
- Can only resume paused agent
- Issue ${issueId} is not claimed
- No pending handoff for issue ${issueId}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/320b60f858d33efd.
Report an issue: GitHub.