chroma-core/chroma · error · TypeError
Weights must be non-negative numbers
Error message
Weights must be non-negative numbers
What it means
Thrown by Rrf (rank.ts:479) when any element of the `weights` array is not a number or is negative. Weights scale each ranker's contribution, so they must be finite non-negative numbers (0 is allowed and effectively disables that ranker's contribution). This runs after the length check, so mismatched lengths throw first.
Source
Thrown at clients/new-js/packages/chromadb/src/execution/expression/rank.ts:479
if (!Number.isInteger(k) || k <= 0) {
throw new TypeError("Rrf k must be a positive integer");
}
if (!Array.isArray(ranks) || ranks.length === 0) {
throw new TypeError("Rrf requires at least one rank expression");
}
const expressions = ranks.map((rank, index) =>
requireRank(rank, `ranks[${index}]`),
);
let weightValues = weights
? weights.slice()
: new Array(expressions.length).fill(1);
if (weightValues.length !== expressions.length) {
throw new Error("Number of weights must match number of ranks");
}
if (weightValues.some((value) => typeof value !== "number" || value < 0)) {
throw new TypeError("Weights must be non-negative numbers");
}
if (normalize) {
const total = weightValues.reduce((sum, value) => sum + value, 0);
if (total <= 0) {
throw new Error(
"Weights must sum to a positive value when normalize=true",
);
}
weightValues = weightValues.map((value) => value / total);
}
const terms = expressions.map((rank, index) => {
const weight = weightValues[index];
const numerator = Val(weight);
const denominator = rank.add(k);
return numerator.divide(denominator);
});View on GitHub (pinned to aecdd12c8a)
Solutions
- Use non-negative numbers: Rrf({ ranks, weights: [0.7, 0.3] })
- Parse and validate config weights before use: nums = String(cfg.w).split(',').map(Number) then check Number.isFinite && >= 0
- Clamp suspect values: weights.map(w => Math.max(0, Number(w) || 0)) if a degraded default is acceptable
Example fix
// before
const fused = Rrf({ ranks, weights: cfg.weights }); // ['0.7','0.3'] from JSON
// after
const weights = cfg.weights.map((w: unknown) => Number(w));
const fused = Rrf({ ranks, weights }); // [0.7, 0.3] Defensive patterns
Strategy: validation
Validate before calling
const ok = weights.every(
(w) => typeof w === 'number' && Number.isFinite(w) && w >= 0,
);
const fused = Rrf({ ranks, weights: ok ? weights : undefined }); Type guard
const isNonNegativeFinite = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v >= 0;
Try / catch
try {
const fused = Rrf({ ranks, weights });
} catch (e) {
if (e instanceof TypeError && e.message.includes('non-negative')) {
return Rrf({ ranks, weights: weights.map(w => Math.max(0, Number(w) || 0)) });
}
throw e;
} Prevention
- Map config strings to Number before passing
- Clamp rebalanced weights: Math.max(0, w)
- Note NaN passes this particular guard — keep your own Number.isFinite check
When it happens
Trigger: Calling Rrf({ ranks: [a, b], weights: [1, -0.5] }) — a negative emphasis; weights: ['0.7', 0.3] — strings from JSON config; weights: [1, NaN] — NaN from a failed computation. Weights containing Infinity also throw, since typeof passes but the value fails the < 0 / type test only for NaN via typeof — NaN passes typeof 'number' but NaN < 0 is false, so NaN weights slip this check and poison the math; the guard reliably catches negatives and non-numbers.
Common situations: Weights typed as strings in YAML/JSON config; sign errors in rebalancing logic (subtracting instead of adding); percentages like -20 from misparsed user input; mixing weight formats ('70%' vs 0.7).
Related errors
- Number of weights must match number of ranks
- Rrf k must be a positive integer
- Rrf requires at least one rank expression
- Weights must sum to a positive value when normalize=true
- All weights must be non-negative
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/ef0a81e564633724.
Report an issue: GitHub.