chroma-core/chroma · error · ValueError
Rank dict must contain exactly one operator, got {len(data)}
Error message
Rank dict must contain exactly one operator, got {len(data)} What it means
A rank dict must contain exactly one operator key ($val, $knn, $sum, ...). Two or more keys in the same dict raise ValueError because the grammar has no sibling-key composition: combining expressions is expressed by nesting them under arithmetic operators, never by merging keys into one dict.
Source
Thrown at chromadb/execution/expression/operator.py:677
- {"$knn": {...}} -> Knn(...)
- {"$sum": [ranks]} -> rank1 + rank2 + ...
- {"$sub": {"left": ..., "right": ...}} -> left - right
- {"$mul": [ranks]} -> rank1 * rank2 * ...
- {"$div": {"left": ..., "right": ...}} -> left / right
- {"$abs": rank} -> abs(rank)
- {"$exp": rank} -> rank.exp()
- {"$log": rank} -> rank.log()
- {"$max": [ranks]} -> rank1.max(rank2).max(rank3)...
- {"$min": [ranks]} -> rank1.min(rank2).min(rank3)...
"""
if not isinstance(data, dict):
raise TypeError(f"Expected dict for Rank, got {type(data).__name__}")
if not data:
raise ValueError("Rank dict cannot be empty")
if len(data) != 1:
raise ValueError(
f"Rank dict must contain exactly one operator, got {len(data)}"
)
op = next(iter(data.keys()))
if op == "$val":
value = data["$val"]
if not isinstance(value, (int, float)):
raise TypeError(f"$val requires a number, got {type(value).__name__}")
return Val(value)
elif op == "$knn":
knn_data = data["$knn"]
if not isinstance(knn_data, dict):
raise TypeError(f"$knn requires a dict, got {type(knn_data).__name__}")
if "query" not in knn_data:
raise ValueError("$knn requires 'query' field")View on GitHub (pinned to aecdd12c8a)
Solutions
- Express combinations with arithmetic operators: {'$sum': [{'$val': 0.5}, {'$knn': {...}}]}.
- Use the class API: Val(0.5) + Knn(query=[...]).
- Never dict-merge (** or |=) rank expressions.
Example fix
# before
Search(rank={'$val': 0.5, '$knn': {'query': emb}}) # 2 keys -> ValueError
# after
Search(rank={'$sum': [{'$val': 0.5}, {'$knn': {'query': emb}}]}) Defensive patterns
Strategy: validation
Validate before calling
def valid_rank_shape(node) -> bool:
return isinstance(node, dict) and len(node) == 1
if rank_cfg is not None and not valid_rank_shape(rank_cfg):
raise ValueError(f'rank must have exactly one operator, got {list(rank_cfg)}')
Search(rank=rank_cfg) Type guard
def is_single_operator_dict(node) -> bool:
return isinstance(node, dict) and len(node) == 1 and next(iter(node), '').startswith('$') Prevention
- One operator per dict - combine via $sum/$mul/$max or the class API's overloaded operators.
- Never **-merge rank dicts.
- Validate user-built rank trees for the single-key rule before calling Search().
When it happens
Trigger: Search(rank={'$val': 0.5, '$knn': {'query': [...]}}); dict-merging two expressions ({**a, **b}); nested case {'$sum': [{'$val': 1, '$abs': {'$val': -1}}]} where the inner dict has 2 keys.
Common situations: Trying to 'add a boost' by merging a constant into an existing Knn dict; config systems that deep-merge rank blocks; assuming sibling keys behave like $and when building weighted scores.
Related errors
- Rank dict cannot be empty
- Expected dict for Rank, got {type(data).__name__}
- $knn requires 'query' field
- $sum requires a list, got {type(ranks_data).__name__}
- Limit offset must be non-negative, got {offset}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/6341fa1afac28a97.
Report an issue: GitHub.