chroma-core/chroma · error · TypeError
$mul requires a list, got {type(ranks_data).__name__}
Error message
$mul requires a list, got {type(ranks_data).__name__} What it means
The $mul rank operator multiplies a chain of rank expressions and, like $sum, expects its payload to be a list of operand dicts. Rank.from_dict raises this TypeError when the $mul payload is not a list/tuple — most commonly a {'left','right'} dict copied from the $sub/$div convention.
Source
Thrown at chromadb/execution/expression/operator.py:784
return result
elif op == "$sub":
sub_data = data["$sub"]
if not isinstance(sub_data, dict):
raise TypeError(
f"$sub requires a dict with 'left' and 'right', got {type(sub_data).__name__}"
)
if "left" not in sub_data or "right" not in sub_data:
raise ValueError("$sub requires 'left' and 'right' fields")
left = Rank.from_dict(sub_data["left"])
right = Rank.from_dict(sub_data["right"])
return left - right
elif op == "$mul":
ranks_data = data["$mul"]
if not isinstance(ranks_data, (list, tuple)):
raise TypeError(
f"$mul requires a list, got {type(ranks_data).__name__}"
)
if len(ranks_data) < 2:
raise ValueError(
f"$mul 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 * r
return result
elif op == "$div":
div_data = data["$div"]
if not isinstance(div_data, dict):
raise TypeError(
f"$div requires a dict with 'left' and 'right', got {type(div_data).__name__}"View on GitHub (pinned to aecdd12c8a)
Solutions
- Use the list form: {"$mul": [rank_a, rank_b, ...]}
- If your builder produces {'left','right'} pairs, convert: {'$mul': [pair['left'], pair['right']]}
- Build the expression in Python as a * b * c and call .to_dict()
- Guard: isinstance(expr['$mul'], (list, tuple)) before deserialization
Example fix
# before
expr = {"$mul": {"left": knn_rank, "right": {"$val": 0.8}}}
# after
expr = {"$mul": [knn_rank, {"$val": 0.8}]} Defensive patterns
Strategy: validation
Validate before calling
def valid_mul_expr(expr):
return (
isinstance(expr, dict) and set(expr) == {"$mul"}
and isinstance(expr["$mul"], (list, tuple))
)
if not valid_mul_expr(rank_expr):
raise ValueError(f"$mul payload must be a list, got {rank_expr!r}") Type guard
def is_mul_expr(d) -> bool:
return (isinstance(d, dict) and set(d) == {"$mul"}
and isinstance(d["$mul"], (list, tuple))) 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
- Keep a cheat sheet of payload shapes: multiplicative ops take operand lists, not left/right dicts
- Generate weighted products in Python: (knn * 0.8).to_dict()
- Unit-test every generated operator dict against Rank.from_dict
When it happens
Trigger: Rank.from_dict({'$mul': {'left': a, 'right': b}}), or {'$mul': scalar}, nested anywhere in a rank expression tree being deserialized.
Common situations: Mixing up the two payload conventions in Chroma's rank DSL: $sum/$mul/$max/$min take lists while $sub/$div take left/right dicts; a generic expression builder that emits the dict shape for every binary operator; hand-edited JSON.
Related errors
- $sub requires a dict with 'left' and 'right', got {type(sub_
- $div requires a dict with 'left' and 'right', got {type(div_
- $abs requires a rank dict, got {type(child_data).__name__}
- $exp requires a rank dict, got {type(child_data).__name__}
- $log requires a rank dict, got {type(child_data).__name__}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/8b036463921a8f91.
Report an issue: GitHub.