chroma-core/chroma · error · ValueError
$max requires at least 2 ranks, got {len(ranks_data)}
Error message
$max requires at least 2 ranks, got {len(ranks_data)} What it means
$max combines at least two rank expressions via successive pairwise maximums, so Chroma's parser rejects a payload with fewer than two operands. This ValueError from Rank.from_dict fires when the $max list has 0 or 1 elements.
Source
Thrown at chromadb/execution/expression/operator.py:842
)
return Rank.from_dict(child_data).exp()
elif op == "$log":
child_data = data["$log"]
if not isinstance(child_data, dict):
raise TypeError(
f"$log requires a rank dict, got {type(child_data).__name__}"
)
return Rank.from_dict(child_data).log()
elif op == "$max":
ranks_data = data["$max"]
if not isinstance(ranks_data, (list, tuple)):
raise TypeError(
f"$max requires a list, got {type(ranks_data).__name__}"
)
if len(ranks_data) < 2:
raise ValueError(
f"$max requires at least 2 ranks, got {len(ranks_data)}"
)
ranks = [Rank.from_dict(r) for r in ranks_data]
result = ranks[0]
for r in ranks[1:]:
result = result.max(r)
return result
elif op == "$min":
ranks_data = data["$min"]
if not isinstance(ranks_data, (list, tuple)):
raise TypeError(
f"$min requires a list, got {type(ranks_data).__name__}"
)
if len(ranks_data) < 2:
raise ValueError(
f"$min requires at least 2 ranks, got {len(ranks_data)}"View on GitHub (pinned to aecdd12c8a)
Solutions
- Pass at least two operands, or drop the wrapper and pass the single rank dict directly
- When clamping against a bound, always include both: {"$max": [rank, {"$val": bound}]}
- Branch when building dynamically: expr = ranks[0] if len(ranks) == 1 else {'$max': ranks}
- Validate operand count before deserialization
Example fix
# before
expr = {"$max": [knn_rank]}
# after
expr = {"$max": [knn_rank, {"$val": 0.0}]} Defensive patterns
Strategy: validation
Validate before calling
def valid_max_expr(expr):
return (
isinstance(expr, dict) and set(expr) == {"$max"}
and isinstance(expr["$max"], (list, tuple))
and len(expr["$max"]) >= 2
)
if not valid_max_expr(rank_expr):
raise ValueError(f"$max needs >= 2 operands: {rank_expr!r}") Type guard
def is_max_expr(d) -> bool:
return (isinstance(d, dict) and set(d) == {"$max"}
and isinstance(d["$max"], (list, tuple))
and len(d["$max"]) >= 2) Try / catch
try:
rank = Rank.from_dict(rank_expr)
except (TypeError, ValueError) as e:
raise ValueError(f"invalid rank expression {rank_expr!r}: {e}") from e Prevention
- A clamp always has two sides: include the bound as {"$val": b} alongside the rank
- Never wrap a lone operand in $max
- Branch on operand count when clamps are built from configurable bound lists
When it happens
Trigger: Rank.from_dict({'$max': []}) or {'$max': [single_rank]} — e.g. a clamp built as max over a dynamic list of bounds where only one bound survives filtering.
Common situations: Clamping code that takes max over a configurable list of upper bounds and ends up with one; wrapping a lone operand 'for consistency'; fixtures trimmed from a two-operand example.
Related errors
- $min requires at least 2 ranks, got {len(ranks_data)}
- $sum requires at least 2 ranks, got {len(ranks_data)}
- $sub requires a dict with 'left' and 'right', got {type(sub_
- $sub requires 'left' and 'right' fields
- $mul requires a list, got {type(ranks_data).__name__}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/f450e6df83ae8e94.
Report an issue: GitHub.