From 9dd9bb8a3f5575a3d3d02ec4c57702e07f946d2f Mon Sep 17 00:00:00 2001 From: Afonso Coutinho Date: Wed, 3 Jun 2026 05:35:09 +0100 Subject: [PATCH] fix: memory recall crashes on a non-dict row from the vector store (#1705) --- services/memory/service.py | 1 + tests/test_memory_recall_nondict_rows.py | 26 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/test_memory_recall_nondict_rows.py diff --git a/services/memory/service.py b/services/memory/service.py index 6eb13c2..d82c81c 100644 --- a/services/memory/service.py +++ b/services/memory/service.py @@ -103,6 +103,7 @@ class MemoryService: metadata=r.get("metadata", {}), ) for r in results + if isinstance(r, dict) ] return MemorySearchResult(memories=memories, query=query, total=len(memories)) diff --git a/tests/test_memory_recall_nondict_rows.py b/tests/test_memory_recall_nondict_rows.py new file mode 100644 index 0000000..29af56c --- /dev/null +++ b/tests/test_memory_recall_nondict_rows.py @@ -0,0 +1,26 @@ +import asyncio + +from services.memory.service import MemoryService + + +class _FakeVectorStore: + """Stands in for MemoryVectorStore.search, which reconstructs rows from a + vector index + metadata store. A stale or corrupt index can yield a + non-dict row mixed in with the good ones.""" + + def search(self, query, k=5): + return [ + {"id": "1", "text": "real memory", "timestamp": 5}, + "corrupt-row", + None, + ] + + +def test_recall_skips_non_dict_vector_rows(tmp_path): + svc = MemoryService(str(tmp_path)) + svc.vector_store = _FakeVectorStore() + res = asyncio.run(svc.recall("anything")) + # old code did r.get(...) on the str/None rows and raised AttributeError, + # losing the whole recall; now only the well-formed row survives. + assert [m.id for m in res.memories] == ["1"] + assert res.total == 1