chroma-core/chroma · error · ValueError
$knn requires exactly one query embedding
Error message
$knn requires exactly one query embedding
What it means
For a dense (list/tuple/ndarray) $knn query, the parser normalizes the input with normalize_embeddings() and requires exactly one embedding row: an empty sequence or multiple rows ([[...], [...]]) raises ValueError. Unlike collection.query(query_embeddings=[...]), the $knn rank operator scores a single query at a time.
Source
Thrown at chromadb/execution/expression/operator.py:713
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")
# Validate the normalized version
validate_embeddings(normalized)
query = normalized[0]
else:
raise TypeError(
f"$knn query must be a list, numpy array, or SparseVector dict, got {type(query).__name__}"
)
key = knn_data.get("key", "#embedding")
if not isinstance(key, str):
raise TypeError(f"$knn key must be a string, got {type(key).__name__}")
limit = knn_data.get("limit", 16)
if not isinstance(limit, int):
raise TypeError(View on GitHub (pinned to aecdd12c8a)
Solutions
- Pass exactly one flat vector [0.1, 0.2, ...] or a single row [[0.1, 0.2]].
- Index into batched encoder output: emb = model.encode([text])[0].
- Guard empty embeddings upstream - skip the search or raise your own error.
Example fix
# before
embs = model.encode(['a', 'b']) # shape (2, d)
Search(rank={'$knn': {'query': embs}}) # -> ValueError
# after
Search(rank={'$knn': {'query': embs[0]}})
# or one text at a time
Search(rank={'$knn': {'query': model.encode([text])[0]}}) Defensive patterns
Strategy: validation
Validate before calling
import numpy as np
def single_query_embedding(emb) -> list:
arr = np.asarray(emb, dtype=float)
if arr.ndim == 2:
if arr.shape[0] != 1:
raise ValueError(f'expected 1 query embedding, got {arr.shape[0]}')
arr = arr[0]
if arr.size == 0:
raise ValueError('query embedding is empty')
return arr.tolist()
Search(rank={'$knn': {'query': single_query_embedding(emb)}}) Prevention
- Know your encoder's output shape - squeeze to 1-D before querying.
- $knn is single-query by design; loop over batches at the caller.
- Treat an empty embedding as a hard pipeline error, not an empty query.
When it happens
Trigger: {'$knn': {'query': []}} (empty); {'$knn': {'query': [[0.1, 0.2], [0.3, 0.4]]}} (batch of 2); feeding an encoder's 2-D batched output directly as the query.
Common situations: Embedding models that always return 2-D arrays ([[...]]); porting collection.query(query_embeddings=[q1, q2]) call sites to Search; empty queries when the embedding service returned nothing.
Related errors
- $knn requires a dict, got {type(knn_data).__name__}
- $knn requires 'query' field
- Expected dict with {TYPE_KEY}='{SPARSE_VECTOR_TYPE_VALUE}',
- $knn query must be a list, numpy array, or SparseVector dict
- $knn limit must be positive, got {limit}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/58ca64ffb9303369.
Report an issue: GitHub.