chroma-core/chroma · error · Error

Weights must sum to a positive value when normalize=true

Error message

Weights must sum to a positive value when normalize=true

What it means

Thrown by Rrf (rank.ts:485) when normalize=true and the weights sum to zero or less. Normalization divides each weight by the total, so a zero total would divide by zero; since negative weights are already rejected by the previous check, this fires in practice when all weights are exactly 0.

Source

Thrown at clients/new-js/packages/chromadb/src/execution/expression/rank.ts:485

  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);
  });

  const fused = terms.reduce((acc, term) => acc.add(term));
  return fused.negate();
};

export const Sum = (...inputs: RankInput[]): RankExpression => {

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Give at least one weight a positive value, or use defaults by omitting weights (all 1s)
  2. Treat all-zero as 'no preference': drop weights and pass normalize: false
  3. Guard before calling: if (normalize && weights.reduce((s, w) => s + w, 0) <= 0) use unweighted ranks

Example fix

// before
const fused = Rrf({ ranks, weights: mix, normalize: true }); // mix = [0, 0]

// after
const total = mix.reduce((s, w) => s + w, 0);
const fused =
  total > 0
    ? Rrf({ ranks, weights: mix, normalize: true })
    : Rrf({ ranks }); // uniform fallback
Defensive patterns

Strategy: validation

Validate before calling

const total = weights.reduce((s, w) => s + w, 0);
const fused =
  total > 0
    ? Rrf({ ranks, weights, normalize: true })
    : Rrf({ ranks }); // all-zero mix: use uniform

Try / catch

try {
  const fused = Rrf({ ranks, weights, normalize: true });
} catch (e) {
  if (e instanceof Error && e.message.includes('positive value')) {
    return Rrf({ ranks, weights, normalize: false });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling Rrf({ ranks: [a, b], weights: [0, 0], normalize: true }) — disabling every ranker while asking for normalized output; dynamic weight schedules (e.g. time-based decay) that hit all-zero; user sliders all set to 0 in a tuning UI.

Common situations: Retriever-mixing UIs where users can zero out every component; softmax/decay computations that underflow to 0; enabling normalize by default while weights come from a sparse config where unset means 0 rather than 1.

Related errors


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