chroma-core/chroma · error · TypeError
$val requires a number, got {type(value).__name__}
Error message
$val requires a number, got {type(value).__name__} What it means
The $val operator holds a constant rank score and its value must be an int or float (bools technically pass since bool subclasses int). Strings like '0.5', None, lists, or nested dicts raise TypeError because there is no coercion in the parser.
Source
Thrown at chromadb/execution/expression/operator.py:686
- {"$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")
query = knn_data["query"]
if isinstance(query, dict):
# SparseVector case - deserialize from transport format
if query.get(TYPE_KEY) == SPARSE_VECTOR_TYPE_VALUE:
query = SparseVector.from_dict(query)
else:
# Old format or invalid - try to construct directlyView on GitHub (pinned to aecdd12c8a)
Solutions
- Use a bare number: {'$val': 0.5}.
- Cast known-clean strings at the boundary: {'$val': float(w)}.
- If the weight is optional, omit the $val term entirely rather than sending None.
Example fix
# before
Search(rank={'$val': boost_str}) # boost_str == '1.5' -> TypeError
# after
Search(rank={'$val': float(boost_str)}) Defensive patterns
Strategy: validation
Validate before calling
weight = request.get('weight')
rank = {'$val': float(weight)} if weight is not None else None
Search(rank=rank) Type guard
def is_numeric_val(node) -> bool:
return (
isinstance(node, dict)
and set(node) == {'$val'}
and isinstance(node['$val'], (int, float))
and not isinstance(node['$val'], bool)
) Prevention
- Type config weights as float in your schema layer.
- Skip optional weights rather than passing None.
- Beware that True/False pass the library's check as 1/0 - never send flags as $val.
When it happens
Trigger: Search(rank={'$val': '0.5'}); {'$val': None}; {'$val': [0.5]}; configs storing weights as strings ('weight': '1.5').
Common situations: YAML/env-var configs where numbers stay strings; JSON with quoted numerics; optional weights defaulting to None instead of being omitted from the expression.
Related errors
- Expected dict for Rank, got {type(data).__name__}
- $knn requires a dict, got {type(knn_data).__name__}
- $sum requires a list, got {type(ranks_data).__name__}
- Expected dict for Limit, got {type(data).__name__}
- Limit offset must be an integer, got {type(offset).__name__}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/498be6690360890f.
Report an issue: GitHub.