Skip to content

Commit 6acf940

Browse files
derekhigginsclaudecdoern
authored
fix(vector_io): propagate search errors instead of returning empty results (#6093)
## Summary - The catch-all `except Exception` in `openai_search_vector_store` was silently swallowing backend errors and returning empty results with HTTP 200 - Clients had no way to distinguish "no matching documents" from "the search failed" - This masked bugs like the milvus-lite 3.0 `chunk_content` KeyError (#6089), where file_search appeared to work but retrieval silently returned nothing - Now re-raises the exception so it surfaces as a 500 to the client - Also fixes `test_openai_vector_store_with_chunks` which was passing `filters={"topic": "ai"}` (the old shorthand format) instead of the typed format `{"type": "eq", "key": "topic", "value": "ai"}` required since #4471. The error was silently swallowed, and the assertion loop over empty results never executed, so the test appeared to pass. Closes #6092 ## Test plan - Added unit test `test_search_vector_store_propagates_backend_errors` that verifies a `KeyError` from the backend propagates to the caller - Fixed `test_openai_vector_store_with_chunks` filter format so it actually exercises the filter path - Reproduced locally with milvus-lite 3.0: before the fix, search returns 200 with empty results; after, returns 500 --------- Signed-off-by: Derek Higgins <derekh@redhat.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Charlie Doern <cdoern@redhat.com>
1 parent 1a0fe23 commit 6acf940

4 files changed

Lines changed: 47 additions & 9 deletions

File tree

src/ogx/providers/remote/vector_io/qdrant/qdrant.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ async def query_vector(
133133
if filters is not None:
134134
raise NotImplementedError("Qdrant provider does not yet support native filtering")
135135

136+
# Collections are created lazily on first insert, so a search against a
137+
# store that has never had chunks added has no collection yet. Treat that
138+
# as an empty result rather than letting Qdrant raise a 404.
139+
if not await self.client.collection_exists(self.collection_name):
140+
return QueryChunksResponse(chunks=[], scores=[])
141+
136142
results = (
137143
await self.client.query_points(
138144
collection_name=self.collection_name,
@@ -184,6 +190,12 @@ async def query_keyword(
184190
if filters is not None:
185191
raise NotImplementedError("Qdrant provider does not yet support native filtering")
186192

193+
# Collections are created lazily on first insert, so a search against a
194+
# store that has never had chunks added has no collection yet. Treat that
195+
# as an empty result rather than letting Qdrant raise a 404.
196+
if not await self.client.collection_exists(self.collection_name):
197+
return QueryChunksResponse(chunks=[], scores=[])
198+
187199
try:
188200
# Use scroll for keyword-only search since query_points requires a query vector
189201
# Scroll allows filtering without a query vector
@@ -264,6 +276,12 @@ async def query_hybrid(
264276
if filters is not None:
265277
raise NotImplementedError("Qdrant provider does not yet support native filtering")
266278

279+
# Collections are created lazily on first insert, so a search against a
280+
# store that has never had chunks added has no collection yet. Treat that
281+
# as an empty result rather than letting Qdrant raise a 404.
282+
if not await self.client.collection_exists(self.collection_name):
283+
return QueryChunksResponse(chunks=[], scores=[])
284+
267285
try:
268286
query_words = query_string.lower().split()
269287
if not query_words:

src/ogx/providers/utils/memory/openai_vector_store_mixin.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,14 +1121,8 @@ async def openai_search_vector_store(
11211121
)
11221122

11231123
except Exception as e:
1124-
# Log the error and return empty results
1125-
logger.error("Error searching vector store", vector_store_id=vector_store_id, error=str(e))
1126-
return VectorStoreSearchResponsePage(
1127-
search_query=request.query if isinstance(request.query, list) else [request.query],
1128-
data=[],
1129-
has_more=False,
1130-
next_page=None,
1131-
)
1124+
logger.error("Failed to search vector store", vector_store_id=vector_store_id, error=str(e))
1125+
raise
11321126

11331127
def _build_reranker_params(
11341128
self,

tests/integration/vector_io/test_openai_vector_stores.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3585,7 +3585,7 @@ def test_openai_vector_store_with_chunks(
35853585
filtered_search = compat_client.vector_stores.search(
35863586
vector_store_id=vector_store.id,
35873587
query="artificial intelligence",
3588-
filters={"topic": "ai"},
3588+
filters={"type": "eq", "key": "topic", "value": "ai"},
35893589
max_num_results=5,
35903590
)
35913591

tests/unit/providers/vector_io/test_vector_io_stores_config.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,32 @@ async def mock_query_chunks(*args, **kwargs):
213213
assert result.search_query == ["test query"] # Original query preserved
214214

215215

216+
async def test_search_vector_store_propagates_backend_errors(vector_io_adapter):
217+
"""Test that exceptions from the vector store backend propagate to the caller."""
218+
vector_store_id = "test_store_error"
219+
vector_io_adapter.openai_vector_stores[vector_store_id] = {
220+
"id": vector_store_id,
221+
"name": "Test Store",
222+
"description": "",
223+
"vector_store_id": "test_db",
224+
"embedding_model": "test/embedding",
225+
}
226+
227+
async def mock_query_chunks(*args, **kwargs):
228+
raise KeyError("chunk_content")
229+
230+
vector_io_adapter.query_chunks = mock_query_chunks
231+
232+
from ogx_api import OpenAISearchVectorStoreRequest
233+
234+
request = OpenAISearchVectorStoreRequest(query="test query", max_num_results=5)
235+
with pytest.raises(KeyError, match="chunk_content"):
236+
await vector_io_adapter.openai_search_vector_store(
237+
vector_store_id=vector_store_id,
238+
request=request,
239+
)
240+
241+
216242
async def test_create_gin_index_executes_correct_sql():
217243
from ogx.providers.remote.vector_io.pgvector.config import PGVectorHNSWVectorIndex
218244
from ogx.providers.remote.vector_io.pgvector.pgvector import PGVectorIndex

0 commit comments

Comments
 (0)