Skip to content

Langchain vector store: list() returns None for non-Chroma clients, crashing Memory.get_all() and Memory.delete_all() #7439

Open

Description

@BlueX888

Component

Vector Store

Summary

Langchain.list() (mem0/vector_stores/langchain.py:196) gates every return behind hasattr(self.client, "_collection"). 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 accepts any existing VectorStore instance, so non-Chroma clients are a supported configuration, and every one of them breaks Memory.get_all() and Memory.delete_all():

  • Memory.get_all() iterates the result at mem0/memory/main.py:1354 (for mem in actual_memories:) → TypeError: 'NoneType' object is not iterable
  • Memory.delete_all() indexes it at mem0/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:474, opensearch.py:406, mongodb.py:410 all return a nested list even when 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 get_all) and mem0/memory/main.py:1925 (the )[0] in delete_all).

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, faiss-cpu 1.15.1, chromadb 1.5.9

How You Verified This

What I Ran

The exact script above, offline: 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.

What I Saw

The verbatim output pasted in "Actual Behavior" above: 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.

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. Every sibling vector store returns a nested list even when empty (faiss.py:619, cassandra.py:474, opensearch.py:406, mongodb.py:410). LangchainConfig.client is typed as "Existing VectorStore instance" — 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 the Chroma-backed client has _collection.
  • Not the except branch: for a non-Chroma client the try block completes without raising — the function simply has no return left on that path.
  • Not caller misuse: the config path used is the documented provider: "langchain" + client wiring, and the identical wiring with a Chroma client works.

The fix direction: the non-Chroma path should return [[]] (the sibling convention; a flat [] would still make delete_all()'s unconditional [0] at main.py:1925 raise IndexError). With that one-line change 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, including a regression test for this).

Happy to be assigned — I can open a PR once this issue carries the accepted label.

AI Assistance

AI found and wrote this, and I have not reproduced it myself. That said, the reproduction above is a self-contained offline script (no credentials, no network) and its output is pasted verbatim, so it can be re-run directly.

Activity

  1. BlueX888 commented on Sep 24, 2026

    @BlueX888
    Author

    Reproduced this. Root cause: mem0/vector_stores/langchain.py:196 gates every return behind hasattr(client,'_collection'); non-Chroma clients fall through to implicit None

    Minimal reproduction:

    PYTHONPATH=$PWD ../mem0.tree/.venv/bin/python ../repro_langchain_list_none.py
    

    Observed output:

    FAISS: list() returns None; get_all() raised TypeError: 'NoneType' object is not iterable; delete_all() raised TypeError: 'NoneType' object is not subscriptable; Chroma control: list() returns [[]], both succeed
    

    Happy to open a PR with this fix, or to be assigned if you prefer to take it.

  2. O96a commented on Sep 24, 2026

    @O96a

    That's a real bug — the hasattr(self.client, "_collection") gate only ever matches Chroma's wrapper, so FAISS/Qdrant/PGVector skip every branch and return implicit None. Proper fix is to drop the gate and dispatch on the actual client type; until that lands, call the underlying store directly (client.index.scroll() for Qdrant, client.similarity_search("") for FAISS) instead of going through Memory.get_all()/delete_all().

  3. YS-OH-CORE commented on Sep 25, 2026

    @YS-OH-CORE

    A nonempty matching-data regression is important before adopting the proposed return [[]]: it removes the exception but can make deletion falsely report success.

    I tested that fallback at 989c7da0 with real Memory methods and a real LangChain FAISS store containing six Alice documents and one Bob document:

                                base             non-Chroma [[]] fallback
    get_all(user_id=Alice)       TypeError        {'results': []}
    delete_all(user_id=Alice)    TypeError        'Memories deleted successfully!'
    Alice records still present 6                6
    

    The actual call uses get_all(filters={'user_id': ...}). I checked retained records by known IDs and content, not through the broken list() path. Memory.get(known_id) still returned the target. As a positive control, Memory.delete(known_id) removed exactly that record and left Bob untouched. A truly empty store produces the same empty/success responses under the fallback, so an empty-only test misses the distinction.

    Executed checker, observations and limits · completed run.

    A fix should implement correct scoped enumeration or explicitly report unsupported enumeration, rather than invent an empty result. An unsupported-operation error must also avoid being swallowed by the method's broad except. The suggested FAISS similarity_search('') is not exhaustive either: the default k=4 returned four of the six matching documents here.

    Scope: real constructor/adapter/FAISS and temporary history; unused LLM/embedder factories and notice callbacks were test doubles. No credentials, model requests, Chroma test, or full upstream suite. The error variant was a design control, not a proposed complete patch. Your original fall-through diagnosis remains valid.

    Check and execution by Zero, an AI collaborator with Youngseok Oh (@YS-OH-CORE).

  4. Souptik96 commented on Sep 26, 2026

    @Souptik96

    Thanks @YS-OH-CORE for the populated-store check: you're right that a [[]] fallback turns the crash into a silent false success. I've updated #7464 so Langchain.list() raises NotImplementedError for non-Chroma clients (outside the broad except), and Chroma listing errors now propagate instead of returning an empty result. There's a regression test with a real FAISS store holding matching records: delete_all() raises and every record stays in place. Could a maintainer take a look and consider labelling this accepted?

  5. YS-OH-CORE commented on Sep 26, 2026

    @YS-OH-CORE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    vector-storeVector store backends (Qdrant, PGVector, Redis, etc.)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions