chroma-core/chroma · error · ValueError
RRF requires at least one rank
Error message
RRF requires at least one rank
What it means
Rrf (Reciprocal Rank Fusion) fuses several ranking strategies by building -sum(weight_i / (k + rank_i)), and its validation lives in to_dict() — so an Rrf(ranks=[]) object constructs fine but raises this ValueError the moment the query is serialized or executed. At least one rank expression is required because the fused sum indexes terms[0].
Source
Thrown at chromadb/execution/expression/operator.py:1204
normalize=True,
k=100
)
"""
ranks: List[Rank]
k: int = 60
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)View on GitHub (pinned to aecdd12c8a)
Solutions
- Pass at least one rank expression, e.g. Rrf(ranks=[Knn(query=..., return_rank=True)])
- Guard at construction time: if not ranks: fall back to a plain (non-RRF) query instead of building Rrf
- Validate before serializing: raise early with your own message if len(ranks) == 0
- Check config/logic that populates ranks so it cannot silently produce an empty list
Example fix
# before
rrf = Rrf(ranks=[], k=60)
query_plan = rrf.to_dict() # ValueError here
# after
if not strategies:
raise ValueError("enable at least one retrieval strategy")
rrf = Rrf(
ranks=[Knn(query=s.query, key=s.key, return_rank=True) for s in strategies],
k=60,
) Defensive patterns
Strategy: validation
Validate before calling
if not ranks:
raise ValueError("RRF needs at least one rank; enable a retrieval strategy")
rrf = Rrf(ranks=[Knn(query=r.query, key=r.key, return_rank=True) for r in ranks], k=60) Type guard
def has_rrf_ranks(ranks) -> bool:
return isinstance(ranks, (list, tuple)) and len(ranks) >= 1 Try / catch
try:
plan = rrf.to_dict()
except ValueError as e:
raise ValueError(f"invalid RRF configuration (ranks={len(rrf.ranks)}, k={rrf.k}): {e}") from e Prevention
- Remember RRF validates at to_dict()/query time, not at construction — check config before you build the query
- Ensure at least one retrieval strategy is enabled when config is dynamic
- Set return_rank=True on Knn ranks fed to RRF so they produce rank outputs
When it happens
Trigger: Rrf(ranks=[], k=60) followed by .to_dict(), or passing such an Rrf into a query — typically because the ranks list is built from a dynamic set of searches that came back empty (e.g. all optional retrieval strategies disabled or filtered out).
Common situations: Configurable hybrid search where every retrieval strategy was toggled off; ranks assembled from per-tenant or per-request config that yields an empty list; refactoring that moves list construction after Rrf creation.
Related errors
- Number of weights ({len(self.weights)}) must match number of
- $sum requires at least 2 ranks, got {len(ranks_data)}
- $mul requires at least 2 ranks, got {len(ranks_data)}
- k must be positive, got {self.k}
- All weights must be non-negative
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/ff24a9ce659af70c.
Report an issue: GitHub.