chroma-core/chroma · error · TypeError
$exp requires a rank dict, got {type(child_data).__name__}
Error message
$exp requires a rank dict, got {type(child_data).__name__} What it means
$exp is a unary rank operator applying the exponential function to one nested rank expression, and Chroma's parser expects its payload to be a single rank dict (as produced by Rank.exp().to_dict()). Rank.from_dict raises this TypeError when the $exp payload is not a dict — e.g. a bare number or a single-element list.
Source
Thrown at chromadb/execution/expression/operator.py:822
if "left" not in div_data or "right" not in div_data:
raise ValueError("$div requires 'left' and 'right' fields")
left = Rank.from_dict(div_data["left"])
right = Rank.from_dict(div_data["right"])
return left / right
elif op == "$abs":
child_data = data["$abs"]
if not isinstance(child_data, dict):
raise TypeError(
f"$abs requires a rank dict, got {type(child_data).__name__}"
)
return abs(Rank.from_dict(child_data))
elif op == "$exp":
child_data = data["$exp"]
if not isinstance(child_data, dict):
raise TypeError(
f"$exp requires a rank dict, got {type(child_data).__name__}"
)
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__}"
)View on GitHub (pinned to aecdd12c8a)
Solutions
- Pass one unwrapped rank dict: {"$exp": {"$val": 2.0}} or {"$exp": {"$mul": [...]}}
- Wrap bare numbers in $val first
- Build in Python as rank_expr.exp().to_dict()
- Guard: isinstance(expr['$exp'], dict) before Rank.from_dict
Example fix
# before
expr = {"$exp": [{"$mul": [knn, {"$val": 2.0}]}]}
# after
expr = {"$exp": {"$mul": [knn, {"$val": 2.0}]}} Defensive patterns
Strategy: validation
Validate before calling
def valid_exp_expr(expr):
return (
isinstance(expr, dict) and set(expr) == {"$exp"}
and isinstance(expr["$exp"], dict)
)
if not valid_exp_expr(rank_expr):
raise ValueError(f"$exp payload must be a single rank dict: {rank_expr!r}") Type guard
def is_exp_expr(d) -> bool:
return (isinstance(d, dict) and set(d) == {"$exp"}
and isinstance(d["$exp"], dict)) 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
- Treat $exp as unary: exactly one nested rank dict, never a list or literal
- Build boosts in Python: weighted.exp().to_dict()
- Keep a round-trip test covering unary operators
When it happens
Trigger: Rank.from_dict({'$exp': 2.0}) or {'$exp': [rank_dict]} anywhere in a deserialized rank expression tree.
Common situations: Hand-written score-transform expressions for boosting (exp of a weighted sum) where the operand is wrapped in a list like $sum operands, or a literal is passed without $val; JSON produced by templating code unfamiliar with the unary-operator shape.
Related errors
- $sub requires a dict with 'left' and 'right', got {type(sub_
- $mul requires a list, got {type(ranks_data).__name__}
- $div requires a dict with 'left' and 'right', got {type(div_
- $abs 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/9da10d28518ee2ed.
Report an issue: GitHub.