headroomlabs-ai/headroom · error · ValueError
query_text provided but HNSWVectorIndex does not embed text.
Error message
query_text provided but HNSWVectorIndex does not embed text. Provide query_vector directly or use an Embedder first.
What it means
Raised by HNSWVectorIndex.search when VectorFilter.query_vector is None but query_text is set. HNSWVectorIndex is a pure vector index with no built-in text embedding, so it cannot convert text to a vector; the caller must embed the text first or pass a query_vector.
Source
Thrown at headroom/memory/adapters/hnsw.py:591
return removed_count
async def search(self, filter: VectorFilter) -> list[VectorSearchResult]:
"""Search for similar memories using vector similarity.
Args:
filter: Vector search filter with query and constraints.
Returns:
List of search results sorted by similarity (descending).
Raises:
ValueError: If neither query_vector nor query_text is provided,
or if query_text is provided (embedding must be done externally).
"""
if filter.query_vector is None:
if filter.query_text is not None:
raise ValueError(
"query_text provided but HNSWVectorIndex does not embed text. "
"Provide query_vector directly or use an Embedder first."
)
raise ValueError("Either query_vector or query_text must be provided")
query_vector = np.asarray(filter.query_vector, dtype=np.float32)
if query_vector.shape[0] != self._dimension:
raise ValueError(
f"Query vector dimension {query_vector.shape[0]} does not match "
f"index dimension {self._dimension}"
)
with self._lock:
# NOTE: Use len() directly, not self.size - Lock is not reentrant!
current_size = len(self._memory_to_hnsw)
if current_size == 0:
return []
View on GitHub (pinned to 322425c43b)
Solutions
- Embed the text first: vec = await embedder.embed(filter.query_text), then search with VectorFilter(query_vector=vec).
- Use a facade/service that pairs an Embedder with the index and accepts text queries.
- If you never need text search, ensure only query_vector is ever set on the filter.
Example fix
// before
results = await index.search(VectorFilter(query_text="hello"))
// after
vec = await embedder.embed("hello")
results = await index.search(VectorFilter(query_vector=vec)) Defensive patterns
Strategy: validation
Validate before calling
if filter.query_vector is None and filter.query_text is not None:
filter.query_vector = await embedder.embed(filter.query_text)
filter.query_text = None
results = await index.search(filter) Prevention
- Keep an Embedder next to the index in one service and always hand vectors to raw indexes.
- Document per-backend whether query_text is supported.
When it happens
Trigger: Calling search(VectorFilter(query_text="...")) directly on HNSWVectorIndex instead of going through a higher-level component that owns an Embedder; porting code from an index that did support text queries.
Common situations: Assuming all VectorIndex implementations embed text; wiring a raw index into a search path without an embedding step; misreading the filter schema.
Related errors
- Either query_vector or query_text must be provided
- Query vector dimension {query_vector.shape[0]} does not matc
- query_text provided but SQLiteVectorIndex does not embed tex
- Memory {memory.id} has no embedding
- Embedding dimension {embedding.shape[0]} does not match inde
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/0b3d0ed0e9df3ea3.
Report an issue: GitHub.