chroma-core/chroma · error · ValueError
Expected dict with {TYPE_KEY}='{SPARSE_VECTOR_TYPE_VALUE}',
Error message
Expected dict with {TYPE_KEY}='{SPARSE_VECTOR_TYPE_VALUE}', got {query} What it means
When $knn's 'query' is a dict, the parser expects the SparseVector transport format, which is tagged with a discriminator: {'#type': 'sparse_vector', 'indices': [...], 'values': [...], 'tokens': [...]} (TYPE_KEY='#type', SPARSE_VECTOR_TYPE_VALUE='sparse_vector', chromadb/base_types.py:8-9). A dict without that exact marker - old formats or hand-built {'indices', 'values'} dicts - is rejected with this ValueError.
Source
Thrown at chromadb/execution/expression/operator.py:705
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")
# 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__}"
)View on GitHub (pinned to aecdd12c8a)
Solutions
- Add the tag: {'#type': 'sparse_vector', **sparse_dict}.
- Build the payload with SparseVector(indices=..., values=...).to_dict() so the tag is always correct.
- For dense queries, pass a list/array instead of a dict.
Example fix
# before
query = {'indices': [0, 2], 'values': [0.1, 0.3]}
Search(rank={'$knn': {'query': query}}) # -> ValueError
# after
from chromadb.base_types import SparseVector
query = SparseVector(indices=[0, 2], values=[0.1, 0.3]).to_dict()
Search(rank={'$knn': {'query': query}}) Defensive patterns
Strategy: validation
Validate before calling
from chromadb.base_types import SparseVector
def sparse_query(indices, values, labels=None):
return SparseVector(indices=indices, values=values, labels=labels).to_dict()
Search(rank={'$knn': {'query': sparse_query([0, 2], [0.1, 0.3])}}) Type guard
from chromadb.base_types import TYPE_KEY, SPARSE_VECTOR_TYPE_VALUE
def is_sparse_vector_dict(d) -> bool:
return isinstance(d, dict) and d.get(TYPE_KEY) == SPARSE_VECTOR_TYPE_VALUE Prevention
- Never hand-write sparse query dicts - use SparseVector.to_dict().
- Store the full to_dict() payload (including #type) when persisting embeddings.
- The discriminator key is '#type' with a hash prefix, not 'type'.
When it happens
Trigger: Search(rank={'$knn': {'query': {'indices': [0, 2], 'values': [0.1, 0.3]}}}) with no #type key; embedding dicts persisted before the type tag existed; sparse queries assembled by hand from tokenizer output.
Common situations: Storing SparseVector fields in your own DB as {indices, values} and feeding them back into $knn; upgrading Chroma versions where older payloads lack the discriminator; using 'type' instead of '#type' as the key.
Related errors
- $knn requires 'query' field
- $knn requires exactly one query embedding
- $knn limit must be positive, got {limit}
- Limit offset must be non-negative, got {offset}
- Limit limit must be positive, got {limit}
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/0a3e7ba265d7809c.
Report an issue: GitHub.