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

  1. Use non-negative numbers: Rrf({ ranks, weights: [0.7, 0.3] })
  2. Parse and validate config weights before use: nums = String(cfg.w).split(',').map(Number) then check Number.isFinite && >= 0
  3. 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

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


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/ef0a81e564633724. Report an issue: GitHub.