chroma-core/chroma · error · ValueError
k must be positive, got {self.k}
Error message
k must be positive, got {self.k} What it means
The k parameter of Chroma's Rrf is the reciprocal-rank smoothing constant (default 60, the standard literature value) that appears as weight_i / (k + rank_i). Rrf.to_dict() validates k > 0 and raises this ValueError at serialization/query time when k is zero or negative, because those values make the fusion terms degenerate or sign-flipped.
Source
Thrown at chromadb/execution/expression/operator.py:1206
)
"""
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)
if weight_sum == 0:
raise ValueError("Sum of weights must be positive when normalize=True")View on GitHub (pinned to aecdd12c8a)
Solutions
- Use a positive k — the standard default is 60; omit the parameter to get 60
- Validate config before constructing: k = k if k and k > 0 else 60
- If k comes from user input, clamp or reject with your own clear error before the query
- Wrap serialization in try/except ValueError to surface which RRF parameter was invalid
Example fix
# before
rrf = Rrf(ranks=ranks, k=int(cfg.get("rrf_k", 0))) # 0 when unset
# after
k = int(cfg.get("rrf_k", 60))
if k <= 0:
raise ValueError(f"rrf_k must be positive, got {k}")
rrf = Rrf(ranks=ranks, k=k) Defensive patterns
Strategy: validation
Validate before calling
k = int(cfg.get("rrf_k", 60))
if k <= 0:
raise ValueError(f"rrf_k must be positive, got {k}")
rrf = Rrf(ranks=ranks, k=k) Type guard
def is_positive_k(k) -> bool:
return isinstance(k, int) and k > 0 Try / catch
try:
plan = rrf.to_dict()
except ValueError as e:
raise ValueError(f"invalid RRF configuration (k={rrf.k}): {e}") from e Prevention
- Default k to 60 and never pass config zeros straight through
- Validate externally supplied k (env vars, CLI flags, request params) as a positive int before building Rrf
- Remember the error surfaces at serialization, so validate early for a clear stack trace
When it happens
Trigger: Rrf(ranks=[...], k=0) or k=-5, then .to_dict() or executing the query that contains it. Commonly k is read from config or a CLI flag that defaults to 0/'not set' and is passed through unchecked.
Common situations: Config plumbing where an unset value becomes 0 (e.g. int(os.environ.get('RRF_K', 0))); tuning scripts sweeping k including 0; a 'disable smoothing' intent incorrectly encoded as k=0.
Related errors
- Number of weights ({len(self.weights)}) must match number of
- RRF requires at least one rank
- All weights must be non-negative
- Sum of weights must be positive when normalize=True
- $sum requires at least 2 ranks, got {len(ranks_data)}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/b0a94b481e8cd42f.
Report an issue: GitHub.