From 25d1a56ce4467e192e07eb0723deaccfdca838ff Mon Sep 17 00:00:00 2001 From: Alex Castillo Date: Tue, 8 Sep 2026 15:12:58 -0400 Subject: [PATCH] fix: skip index build when a source produces no text chunks `Retriever.index()` appends chunks and then always calls `_build_faiss()` (for `use_faiss=True`). A source that yields no text -- an empty file, a scanned/image-only PDF, a blank string -- leaves `self.chunks` empty, and `_build_faiss()` then embeds `[]` and reads `vectors.shape[1]`, raising `IndexError: tuple index out of range` (or `ValueError: need at least one array to concatenate` for the local embedder). `index()` now logs a warning and returns early when there is nothing to index, and `_query_faiss()` returns `[]` when the FAISS index was never built instead of raising from `_get_faiss()`. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GtYgwYNfJ3wHrw9xrooVoB --- fenn/agents/rag/retriever.py | 8 ++++++++ tests/unit/agents/test_retrievers.py | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/fenn/agents/rag/retriever.py b/fenn/agents/rag/retriever.py index 133b2a7..62d6bd4 100644 --- a/fenn/agents/rag/retriever.py +++ b/fenn/agents/rag/retriever.py @@ -262,6 +262,12 @@ def index(self, docs): for chunk in chunk_text(doc, mode=self.chunk_mode): self.chunks.append(chunk) + if not self.chunks: + logger.warning( + "[cofone] nothing to index: the source(s) produced no text chunks." + ) + return + if self.use_faiss: self._build_faiss() if self.persist_path: @@ -344,6 +350,8 @@ def query(self, text, top_k=5): return self._query_bm25(text, top_k) def _query_faiss(self, text, top_k): + if self._faiss_index is None or not self.chunks: + return [] faiss_mod = self._get_faiss() vec = self._embed([text], input_type="search_query") faiss_mod.normalize_L2(vec) diff --git a/tests/unit/agents/test_retrievers.py b/tests/unit/agents/test_retrievers.py index eabbf60..976756e 100644 --- a/tests/unit/agents/test_retrievers.py +++ b/tests/unit/agents/test_retrievers.py @@ -215,6 +215,31 @@ def test_build_faiss_raises_without_faiss(self): r._build_faiss() +class TestIndexWithoutChunks: + def test_faiss_index_with_no_docs_does_not_build(self): + r = Retriever(use_faiss=True) + with patch.object(r, "_build_faiss") as build: + r.index([]) + build.assert_not_called() + assert r._faiss_index is None + + def test_faiss_index_with_only_blank_docs_does_not_build(self): + r = Retriever(use_faiss=True) + with patch.object(r, "_build_faiss") as build: + r.index([" \n\n "]) + build.assert_not_called() + assert r.chunks == [] + + def test_bm25_index_with_no_docs_does_not_raise(self): + r = Retriever(use_faiss=False) + r.index([]) + assert r.query("anything") == [] + + def test_query_faiss_returns_empty_when_index_never_built(self): + r = Retriever(use_faiss=True) + assert r.query("anything", top_k=5) == [] + + # ── Embedding method ImportError paths ────────────────────────────────────────