chroma-core/chroma · error · ValueError
Expected where document operand value for operator {operator
Error message
Expected where document operand value for operator {operator} to be a str, got {operand} What it means
In validate_where_document, when the operator is one of $contains, $not_contains, $regex, $not_regex (i.e. not a logical operator), the operand must be a Python str. Passing any non-string (int, list, dict, None) raises this ValueError before the query executes.
Source
Thrown at chromadb/api/types.py:1326
"$or",
]:
raise ValueError(
f"Expected where document operator to be one of $contains, $not_contains, $regex, $not_regex, $and, $or, got {operator}"
)
if operator == "$and" or operator == "$or":
if not isinstance(operand, list):
raise ValueError(
f"Expected document value for $and or $or to be a list of where document expressions, got {operand}"
)
if len(operand) <= 1:
raise ValueError(
f"Expected document value for $and or $or to be a list with at least two where document expressions, got {operand}"
)
for where_document_expression in operand:
validate_where_document(where_document_expression)
# Value is $contains/$not_contains/$regex/$not_regex operator
elif not isinstance(operand, str):
raise ValueError(
f"Expected where document operand value for operator {operator} to be a str, got {operand}"
)
elif len(operand) == 0:
raise ValueError(
f"Expected where document operand value for operator {operator} to be a non-empty str"
)
def validate_include(include: Include, dissalowed: Optional[Include] = None) -> None:
"""Validates include to ensure it is a list of strings. Since get does not allow distances, allow_distances is used
to control if distances is allowed"""
if not isinstance(include, list):
raise ValueError(f"Expected include to be a list, got {include}")
for item in include:
if not isinstance(item, str):
raise ValueError(f"Expected include item to be a str, got {item}")
View on GitHub (pinned to aecdd12c8a)
Solutions
- Pass a plain string operand: where_document={"$contains": "hello"}
- For multiple alternatives use {"$or": [{"$contains": "a"}, {"$contains": "b"}]}
- Cast/validate the value before the call: operand = str(operand) if operand is not None else skip the filter
Example fix
# before
where_document = {"$contains": ["hello", "world"]}
# after
where_document = {"$or": [
{"$contains": "hello"},
{"$contains": "world"},
]} Defensive patterns
Strategy: type-guard
Validate before calling
def normalize_where_document(wd: dict) -> dict:
out = {}
for op, operand in wd.items():
if op in ("$contains", "$not_contains", "$regex", "$not_regex"):
if not isinstance(operand, str):
raise TypeError(f"{op} needs a str operand, got {type(operand).__name__}")
out[op] = operand
else:
out[op] = operand
return out
res = collection.get(where_document=normalize_where_document(wd)) Type guard
STRING_OPS = {"$contains", "$not_contains", "$regex", "$not_regex"}
def is_valid_where_document(wd: object) -> bool:
if not isinstance(wd, dict) or len(wd) != 1:
return False
op, operand = next(iter(wd.items()))
if op in STRING_OPS:
return isinstance(operand, str) and len(operand) > 0
if op in ("$and", "$or"):
return isinstance(operand, list) and len(operand) >= 2 and all(is_valid_where_document(e) for e in operand)
return False Try / catch
try:
res = collection.get(where_document=wd)
except ValueError as e:
if "to be a str, got" in str(e):
raise ValueError(f"bad where_document operand: {wd!r}") from e
raise Prevention
- Remember there is no implicit list-OR: multiple terms need $or with $contains clauses
- Type-annotate filter builders (dict[str, str]) so mypy catches non-str operands
- Validate deserialized request payloads before forwarding them as filters
When it happens
Trigger: collection.query(query_embeddings=..., where_document={"$contains": 123}) or {"$not_contains": ["a", "b"]} or {"$regex": None}. Also happens when a variable used as the operand is unexpectedly None or a list, e.g. an unrolled parameter from an API request.
Common situations: Passing a list expecting OR-semantics (Chroma needs $or with multiple $contains clauses); a template/config value that arrived as JSON number or null; forgetting that $regex takes the pattern string itself, not a compiled pattern or a (pattern, flags) tuple.
Related errors
- Expected document value for $and or $or to be a list with at
- Expected where document operand value for operator {operator
- Expected include to be a list, got {include}
- Expected include item to be a str, got {item}
- Expected embeddings to be a list, got {type(embeddings).__na
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/bea4173622e9625e.
Report an issue: GitHub.