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

  1. Pass exactly one flat vector [0.1, 0.2, ...] or a single row [[0.1, 0.2]].
  2. Index into batched encoder output: emb = model.encode([text])[0].
  3. 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

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


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/58ca64ffb9303369. Report an issue: GitHub.