Repository navigation
Description
Component
Vector Store
Summary
Langchain.list() (mem0/vector_stores/langchain.py:191) gates every return behind hasattr(self.client, "_collection") (mem0/vector_stores/langchain.py:196). For any langchain VectorStore that is not Chroma-backed (FAISS, Qdrant, PGVector, ...), the function falls off the end and returns None — not even []. LangchainConfig.client is typed as "Existing VectorStore instance" (mem0/configs/vector_stores/langchain.py:16), so any langchain VectorStore — not only Chroma — is a supported configuration, and every non-Chroma client breaks Memory.get_all() and Memory.delete_all():
Memory.get_all()iterates the result atmem0/memory/main.py:1354(for mem in actual_memories:) →TypeError: 'NoneType' object is not iterableMemory.delete_all()indexes it atmem0/memory/main.py:1923-1925(self.vector_store.list(filters=filters, top_k=DELETE_ALL_BATCH_SIZE)[0]) →TypeError: 'NoneType' object is not subscriptable
Steps to Reproduce
Fully offline (no API key, no network; FakeEmbeddings avoids any model calls):
from langchain_community.vectorstores import FAISS
from langchain_core.embeddings import FakeEmbeddings
from mem0 import Memory
faiss_store = FAISS.from_texts(["hello", "world"], embedding=FakeEmbeddings(size=8))
print("FAISS store has '_collection' attr:", hasattr(faiss_store, "_collection"))
config = {
"vector_store": {
"provider": "langchain",
"config": {"client": faiss_store, "collection_name": "mem0"},
},
"embedder": {"provider": "openai",
"config": {"api_key": "dummy", "embedding_dims": 1536}},
"llm": {"provider": "openai",
"config": {"api_key": "dummy", "model": "gpt-4o-mini"}},
}
m = Memory.from_config(config)
print("vector_store.list() returns:", m.vector_store.list())
try:
m.get_all(filters={"user_id": "alice"})
print("get_all(): succeeded")
except Exception as e:
print(f"get_all() raised {type(e).__name__}: {e}")
try:
m.delete_all(user_id="alice")
print("delete_all(): succeeded")
except Exception as e:
print(f"delete_all() raised {type(e).__name__}: {e}")
# control: a real Chroma client through the same Langchain wrapper
import chromadb
from langchain_community.vectorstores import Chroma
print()
print("=== control: real Chroma client through the same Langchain store ===")
chroma_store = Chroma(collection_name="mem0",
embedding_function=FakeEmbeddings(size=8),
client=chromadb.EphemeralClient())
print("Chroma store has '_collection' attr:", hasattr(chroma_store, "_collection"))
m2 = Memory.from_config({**config, "vector_store": {
"provider": "langchain",
"config": {"client": chroma_store, "collection_name": "mem0"}}})
print("vector_store.list() returns:", m2.vector_store.list())
print("get_all():", m2.get_all(filters={"user_id": "alice"}))
print("delete_all() count:", m2.delete_all(user_id="alice"))Expected Behavior
list() returns a (nested) list for every client, as VectorStoreBase.list() documents — "List all memories." (mem0/vector_stores/base.py:59-61) — and as every sibling implementation does: faiss.py:619, cassandra.py:480, opensearch.py:408, mongodb.py:413 all return a nested list (return [[]]) even when the store is empty. With that, get_all() returns {'results': []} and delete_all() completes, exactly as the Chroma control group does.
Actual Behavior
Actual output (mem0 2.0.20 from source @ c7ee362a, Python 3.12.14, macOS; langchain 1.4.2, langchain-community 0.4.2, faiss-cpu 1.15.1, chromadb 1.5.9):
FAISS store has '_collection' attr: False
vector_store.list() returns: None
get_all() raised TypeError: 'NoneType' object is not iterable
delete_all() raised TypeError: 'NoneType' object is not subscriptable
=== control: real Chroma client through the same Langchain store ===
Chroma store has '_collection' attr: True
vector_store.list() returns: [[]]
get_all(): {'results': []}
delete_all() count: {'message': 'Memories deleted successfully!'}
The tracebacks land at mem0/memory/main.py:1354 (for mem in actual_memories: in the get_all path) and mem0/memory/main.py:1925 (the )[0] in delete_all).
Root Cause
mem0/vector_stores/langchain.py:191-208: every return inside list() sits either inside the if hasattr(self.client, "_collection") and hasattr(self.client._collection, "get") branch or inside the except. For a non-Chroma client the try block completes without raising and without executing any return, so the function falls off the end and Python returns None. Non-Chroma langchain clients expose no generic "list all vectors" API, so the correct behaviour is the sibling convention: return an empty nested list, return [[]] (a flat [] would still make delete_all()'s unconditional [0] at main.py:1925 raise IndexError).
Environment
- mem0 version: 2.0.20 (from source, main @
c7ee362a) - Python 3.12.14, macOS (darwin, arm64)
- langchain 1.4.2, langchain-community 0.4.2, langchain-core 1.6.5, faiss-cpu 1.15.1, chromadb 1.5.9
How You Verified This
What I Ran
The exact script above, offline, against the unpatched main @ c7ee362a: a FAISS VectorStore built locally with FakeEmbeddings, wrapped in Memory via vector_store.provider = "langchain"; then the same wrapper around a real (ephemeral) Chroma client as a control. I also added a regression test that wraps a Mock(spec=VectorStore) (no _collection attribute, like every non-Chroma client) and asserts list() returns a list whose first element is a list.
What I Saw
The verbatim output pasted in "Actual Behavior": list() returns None on the FAISS client, get_all() raises TypeError: 'NoneType' object is not iterable, delete_all() raises TypeError: 'NoneType' object is not subscriptable; the Chroma control returns [[]] and both methods succeed. The regression test fails on the unpatched base:
E AssertionError: expected a list, got <class 'NoneType'>
FAILED tests/vector_stores/test_langchain_vector_store.py::test_list_returns_empty_list_for_non_chroma_client
Why This Is a Bug
VectorStoreBase.list()'s contract is "List all memories." (mem0/vector_stores/base.py:59-61), and the consumers in main.py assume a list (for mem in actual_memories: at 1354, unconditional [0] at 1925). Every sibling vector store returns a nested list even when empty (faiss.py:619, cassandra.py:480, opensearch.py:408, mongodb.py:413). LangchainConfig.client is typed as "Existing VectorStore instance" (mem0/configs/vector_stores/langchain.py:16) — any langchain VectorStore, not only Chroma — so the non-Chroma path is supported configuration, and it returns None.
What I Ruled Out
- Not a FAISS-specific quirk: the gate is
hasattr(self.client, "_collection"), so any non-Chroma langchain client (Qdrant, PGVector, ...) takes the same fall-through; only Chroma-backed clients carry_collection. - Not the
exceptbranch: for a non-Chroma client thetryblock completes without raising — the function simply has noreturnleft on that path. - Not caller misuse: the config path used is the documented
provider: "langchain"+clientwiring, and the identical wiring with a Chroma client works.
With the one-line return [[]] fix I re-ran the script: list() returns [[]], get_all() returns {'results': []}, delete_all() succeeds, and tests/vector_stores/test_langchain_vector_store.py passes (15 passed, 1 warning in 0.33s, including the regression test).
Happy to open a PR with this fix once this issue is accepted, or to be assigned if you prefer to take it.
AI Assistance
AI found and wrote this, and I have not reproduced it myself — the reproduction output above is captured verbatim from actual runs of the script and tests by the AI tooling.
Activity
BlueX888 commentedon Sep 26, 2026
Reproduced this. Root cause: mem0/vector_stores/langchain.py:196 gates every return in list() behind the Chroma-only hasattr(self.client, "_collection") check, so any non-Chroma VectorStore (FAISS, Qdrant, PGVector, ...) falls off the end and returns None; Memory.get_all() then crashes iterating it (mem0/memory/main.py:1354) and Memory.delete_all() crashes indexing it (mem0/memory/main.py:1925).
Minimal reproduction:
Observed output:
Fix is ready on
BlueX888:fix/prep-langchain-vector-store-list-returns-none(commit55cc1423). Return [[]] on the non-Chroma path, matching the empty-result convention of the sibling vector stores (faiss.py:619, cassandra.py:480, opensearch.py:408, mongodb.py:413), so get_all()/delete_all() see an empty result instead of None.Verified with:
python -m pytest tests/vector_stores/test_langchain_vector_store.py (15 passed, incl. the new regression test test_list_returns_empty_list_for_non_chroma_client)Happy to open the PR once you confirm the approach or assign this to me.
chenhz01 commentedon Sep 30, 2026
Hi @BlueX888 - the
Langchain.list()gate onhasattr(self.client, "_collection")is the culprit: any non-Chroma langchain store (FAISS, Qdrant, PGVector) falls through and returnsNone, soget_all()iteratesNoneanddelete_all()indexesNone[0]- bothTypeError.Here is a wrapper that always returns a list:
It uses Chroma's
_collectionwhen present, else falls back to the store'sdocstore/index_to_docstore_id. If useful I can extend it to reconstruct minimal payloads fordelete_all().BlueX888 commentedon Oct 17, 2026
Correction to my earlier direction here: an unconditional empty-result fallback is itself the bug — it makes
get_all()return{'results': []}even when matching records exist and letsdelete_all()report success while deleting nothing (exactly what @YS-OH-CORE demonstrated on #7439). I've reworked the branch accordingly:Langchain.list()now dispatches on the actual client, per @O96a's proper-fix note: the Chroma path is unchanged (empty result stays[[]], listing errors are logged and re-raised instead of swallowed); docstore-backed clients (FAISS and friends) are really enumerated —(key, doc)pairs from the docstore, mem0 filters applied withfaiss.py_apply_filterssemantics before thetop_kcap — soget_all()returns the matching memories anddelete_all()actually deletes them; every other client raisesNotImplementedErrornaming the client type, raised outside thetryso the broad Chromaexceptcan't swallow it.listreturns the 3 alice entries with docstore-key ids;Memory.get_allreturns them instead of{'results': []};delete_allremoves exactly alice and leaves bob),top_kcapping/uncapping, correct-empty[[]]for a genuinely empty store, andNotImplementedErrorfor unenumerable clients. The old test that pinned the wrong-empty behavior is gone.Holding off on opening the PR until #7439 picks up the
acceptedlabel so the triage gate doesn't close it (it closed #7464 for exactly that). Branch:fix/prep-langchain-vector-store-list-returns-none(rebased on current main).BlueX888 commentedon Oct 17, 2026
Quick clarification on my previous comment so nobody chases a stale branch: the reworked implementation (three-way dispatch on the actual client — Chroma path kept, docstore-backed clients like FAISS enumerated from their real docstore, unenumerable clients raising
NotImplementedErrornamed for the client type) is prepared and validated with red/green tests against currentmain(0724291), but the branch is deliberately not pushed yet: this repo's PR gate auto-closes PRs whose referenced issue lacks the maintaineracceptedlabel (see #7464). I'll push and open the PR referencingFixes #7439as soon as this issue is triaged (assigned or labeledaccepted/prs welcome).O96a commentedon Oct 20, 2026
The
_collectioncheck is Chroma internals leaking into a generic wrapper — langchain'sVectorStorebase class has no list-all method, so there's nothing portable to duck-type on, and every non-Chroma backend falls straight through toNone.Two ways out. Per-backend branches work — Chroma
_collection.get(include=[]), Qdrantclient.scroll(limit=..., offset=...), PGVector a plainSELECT id, payload— but that's exactly the maintenance tax you're trying to avoid. I'd take the other route: keep mem0's own id→payload sidecar (the same store already written on insert) and havelist()/delete_all()read from that, then hand the ids toclient.delete(ids=...). Enumeration is a metadata problem, not a similarity-search problem, so it shouldn't go through the vector client at all.Either way,
list()should raise a clearNotImplementedErrorwhen a backend genuinely can't enumerate. A silentNonethat turns intoTypeError: 'NoneType' is not iterablethree frames later is the worst outcome here.