chroma-core/chroma · error · ValueError
$knn requires 'query' field
Error message
$knn requires 'query' field
What it means
The 'query' field is the only required member of the $knn options dict - it carries the query embedding (dense list/array or serialized SparseVector). Omitting it raises ValueError before anything else is checked; key/limit/return_rank all have defaults ('#embedding', 16, False).
Source
Thrown at chromadb/execution/expression/operator.py:695
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 directly
raise ValueError(
f"Expected dict with {TYPE_KEY}='{SPARSE_VECTOR_TYPE_VALUE}', got {query}"
)
elif isinstance(query, (list, tuple, np.ndarray)):
# Dense vector case - normalize then validate
normalized = normalize_embeddings(query)
if not normalized or len(normalized) > 1:
raise ValueError("$knn requires exactly one query embedding")View on GitHub (pinned to aecdd12c8a)
Solutions
- Add the query vector: {'$knn': {'query': emb, ...}}.
- Check for exact spelling - the key must be literally 'query'.
- If the embedding is unavailable, skip the ranked query (rank=None) instead of sending a stub.
Example fix
# before
Search(rank={'$knn': {'key': '#embedding', 'limit': 10}}) # -> ValueError
# after
Search(rank={'$knn': {'query': emb, 'key': '#embedding', 'limit': 10}}) Defensive patterns
Strategy: validation
Validate before calling
if not isinstance(knn_opts, dict) or 'query' not in knn_opts:
raise ValueError("$knn requires 'query' with exactly one embedding")
Search(rank={'$knn': knn_opts}) Type guard
def has_knn_query(knn_opts) -> bool:
return isinstance(knn_opts, dict) and 'query' in knn_opts Try / catch
try:
Search(rank={'$knn': knn_opts})
except (TypeError, ValueError) as e:
return bad_request(f'invalid $knn expression: {e}') Prevention
- Set 'query' first when building $knn options.
- Name the variable holding the embedding 'query' to mirror the wire key.
- Fail fast at your API edge when the caller supplies no embedding.
When it happens
Trigger: Search(rank={'$knn': {'key': '#embedding', 'limit': 10}}); {'$knn': {}}; renamed or typo'd fields like {'queries': [...]} or {'embedding': [...]} instead of 'query'.
Common situations: Integrating with an internal search API whose field names differ; async pipelines where the embedding fetch failed and the field was never attached; copying a $knn example and deleting the query while testing other options.
Related errors
- Rank dict cannot be empty
- Rank dict must contain exactly one operator, got {len(data)}
- $knn requires a dict, got {type(knn_data).__name__}
- Expected dict with {TYPE_KEY}='{SPARSE_VECTOR_TYPE_VALUE}',
- $knn requires exactly one query embedding
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/39f8e58abd164e19.
Report an issue: GitHub.