chroma-core/chroma · error · ValueError
Number of weights ({len(self.weights)}) must match number of
Error message
Number of weights ({len(self.weights)}) must match number of ranks ({len(self.ranks)}) What it means
When weights are supplied to Chroma's Rrf, every ranking strategy must have exactly one weight so that zip(weights, ranks) pairs them for the terms weight_i / (k + rank_i). Rrf.to_dict() raises this ValueError at serialization time when len(weights) != len(ranks) — including weights=[] against a non-empty ranks list, since validation runs before defaults are applied.
Source
Thrown at chromadb/execution/expression/operator.py:1211
weights: Optional[List[float]] = None
normalize: bool = False
def to_dict(self) -> Dict[str, Any]:
"""Convert RRF to a composition of existing expression operators.
Builds: -sum(weight_i / (k + rank_i)) for each rank
Using Python's overloaded operators for cleaner code.
"""
# Validate RRF parameters
if not self.ranks:
raise ValueError("RRF requires at least one rank")
if self.k <= 0:
raise ValueError(f"k must be positive, got {self.k}")
# Validate weights if provided
if self.weights is not None:
if len(self.weights) != len(self.ranks):
raise ValueError(
f"Number of weights ({len(self.weights)}) must match number of ranks ({len(self.ranks)})"
)
if any(w < 0.0 for w in self.weights):
raise ValueError("All weights must be non-negative")
# Populate weights with 1.0 if not provided
weights = self.weights if self.weights else [1.0] * len(self.ranks)
# Normalize weights if requested
if self.normalize:
weight_sum = sum(weights)
if weight_sum == 0:
raise ValueError("Sum of weights must be positive when normalize=True")
weights = [w / weight_sum for w in weights]
# Zip weights with ranks and build terms: weight / (k + rank)
terms = [w / (self.k + rank) for w, rank in zip(weights, self.ranks)]
View on GitHub (pinned to aecdd12c8a)
Solutions
- Make weights match ranks one-to-one, e.g. Rrf(ranks=[a, b], weights=[1.0, 1.0])
- Prefer omitting weights entirely for equal weighting — Chroma then uses 1.0 per rank
- Build weights from the same source as ranks: weights = [cfg[s.name] for s in strategies]
- Assert len(weights) == len(ranks) before constructing/serializing Rrf
Example fix
# before
strategies = [dense, sparse, full_text] # grew to 3
rrf = Rrf(ranks=[Knn(query=s.query, return_rank=True) for s in strategies],
weights=[1.0, 1.0]) # still 2
# after
ranks = [Knn(query=s.query, return_rank=True) for s in strategies]
rrf = Rrf(ranks=ranks, weights=[1.0] * len(ranks)) Defensive patterns
Strategy: validation
Validate before calling
ranks = [Knn(query=s.query, key=s.key, return_rank=True) for s in strategies]
if weights is not None and len(weights) != len(ranks):
raise ValueError(f"{len(weights)} weights for {len(ranks)} ranks; must match")
rrf = Rrf(ranks=ranks, weights=weights, k=60) Type guard
def weights_match_ranks(weights, ranks) -> bool:
return weights is None or (isinstance(weights, (list, tuple)) and len(weights) == len(ranks)) Try / catch
try:
plan = rrf.to_dict()
except ValueError as e:
raise ValueError(
f"invalid RRF configuration: {len(rrf.weights or [])} weights vs "
f"{len(rrf.ranks)} ranks"
) from e Prevention
- Derive weights from the same strategy list that builds ranks, or omit weights for equal weighting
- When adding a retrieval strategy, update the weights config in the same change
- Assert length equality at config-load time so failures happen at startup, not at query time
When it happens
Trigger: Rrf(ranks=[knn1, knn2], weights=[1.0]) (one weight for two ranks), or weights=[] with non-empty ranks; typically ranks are built from a dynamic strategy list while weights come from static config, and the two drift out of sync.
Common situations: Adding a new retrieval strategy to hybrid search without extending the weights config; weights loaded from JSON/YAML that was written for an older strategy set; per-environment config divergence between staging and production.
Related errors
- RRF requires at least one rank
- k must be positive, got {self.k}
- $sum requires at least 2 ranks, got {len(ranks_data)}
- $mul requires at least 2 ranks, got {len(ranks_data)}
- All weights must be non-negative
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/6e2b32e960e1b4cc.
Report an issue: GitHub.