chroma-core/chroma · error · ValueError
Expected where to have exactly one operator, got {where}
Error message
Expected where to have exactly one operator, got {where} What it means
A ChromaDB `where` clause must contain exactly one operator at each level: either one field expression ("field": value/operatorexpr) or one logical operator ($and/$or). This ValueError fires when the dict has zero or two-plus keys, e.g. {"a": 1, "b": 2} or {}. The single-key grammar is what allows the validator to distinguish a field filter from a logical combinator, and it is enforced recursively.
Source
Thrown at chromadb/api/types.py:1194
def validate_metadatas(metadatas: Metadatas) -> Metadatas:
"""Validates metadatas to ensure it is a list of dictionaries of strings to strings, ints, floats or bools"""
if not isinstance(metadatas, list):
raise ValueError(f"Expected metadatas to be a list, got {metadatas}")
for metadata in metadatas:
validate_metadata(metadata)
return metadatas
def validate_where(where: Where) -> None:
"""
Validates where to ensure it is a dictionary of strings to strings, ints, floats or operator expressions,
or in the case of $and and $or, a list of where expressions
"""
if not isinstance(where, dict):
raise ValueError(f"Expected where to be a dict, got {where}")
if len(where) != 1:
raise ValueError(f"Expected where to have exactly one operator, got {where}")
for key, value in where.items():
if not isinstance(key, str):
raise ValueError(f"Expected where key to be a str, got {key}")
# $contains and $not_contains are only valid as operators within a
# field expression (e.g. {"field": {"$contains": val}}), not as
# top-level where keys.
if key in ("$contains", "$not_contains"):
raise ValueError(
f"Expected where key to be a metadata field name or a logical "
f"operator ($and, $or), got {key}"
)
if (
key != "$and"
and key != "$or"
and key != "$in"
and key != "$nin"
and not isinstance(value, (str, int, float, dict))
):View on GitHub (pinned to aecdd12c8a)
Solutions
- Rewrite multi-field filters with $and: {"$and": [{"source": "wiki"}, {"year": 2024}]} (list needs >= 2 entries).
- Build filters incrementally by appending single-key dicts to a list and wrapping in $and only if len > 1; pass where=None when the list is empty.
- Check for typos that accidentally merge keys, e.g. {**base, **extra} on where dicts.
Example fix
# before
where = {"source": "wiki", "year": 2024}
# after
where = {"$and": [{"source": "wiki"}, {"year": 2024}]} Defensive patterns
Strategy: validation
Validate before calling
def build_where(conditions: list[dict]) -> dict | None:
"""Combine single-key condition dicts into a valid Chroma where."""
if not conditions:
return None
if len(conditions) == 1:
return conditions[0]
return {"$and": conditions}
# usage: every element must itself have exactly one key
assert all(len(c) == 1 for c in conditions) Type guard
def is_single_key(where: dict) -> bool:
return len(where) == 1 Try / catch
try:
result = collection.get(where=where)
except ValueError as e:
if "exactly one operator" in str(e):
# flatten multi-key dict into $and and retry once
conds = [{k: v} for k, v in where.items()]
where = conds[0] if len(conds) == 1 else {"$and": conds}
result = collection.get(where=where)
else:
raise Prevention
- Never dict-merge conditions ({**a, **b}); append to a list instead.
- Centralize filter construction in a builder that enforces the one-key rule.
- Wrap condition lists in $and only when len >= 2; use the condition directly when len == 1.
- Property-test your filter builder: every output must pass validate_where.
When it happens
Trigger: Passing where={"source": "wiki", "year": 2024} (two fields, no $and), where={} (empty dict, often from a filter builder that added nothing), or nesting a multi-key dict inside $and, e.g. {"$and": [{"a": 1, "b": 2}]}.
Common situations: Developers assuming Mongo-style multi-key filtering where {"a":1,"b":2} means implicit AND; auto-generated filters where each optional predicate is dict-merged (d1 | d2) instead of appended to an $and list; empty filters produced when no user filters are selected but the code still passes `where`.
Related errors
- Expected where to be a dict, got {where}
- Expected where to be a non-empty object
- Expected 'where' to have exactly one operator, but got ${Obj
- Exactly one of {', '.join(contains_one)} must be provided
- At least one of ${recordSetFields.join(", ")} must be provid
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/d43ef10930c57a9b.
Report an issue: GitHub.