Skip to content

Conversation

@peisong13
Copy link

Fix: Memory.delete_all() incorrectly wiping entire vector store collection

Problem Description

The Memory.delete_all() method had a critical bug where it was calling self.vector_store.reset() after deleting filtered memories. This caused the entire vector store collection to be wiped, not just the memories that matched the provided filters (user_id, agent_id, run_id).

Impact

  • Data Loss: When users called delete_all(user_id="user1"), it would delete user1's memories as expected, but then wipe ALL memories from ALL users
  • Unexpected Behavior: The method name suggests filtering, but the implementation caused a complete reset
  • Production Risk: This could cause catastrophic data loss in production environments

Root Cause

In mem0/memory/main.py, line 1053, the delete_all() method contained:

# delete vector memories by filter
memories = self.vector_store.list(filters=filters)[0]
for memory in memories:
    self._delete_memory(memory.id)
# self.vector_store.reset()  # <-- This line was the problem

The self.vector_store.reset() call was incorrectly resetting the entire vector store collection after the filtered deletion.

Solution

Simple Fix: Comment out the problematic self.vector_store.reset() line.

Why This Fix Is Correct

  1. Filtered Deletion Already Works: The code above the reset call correctly:

    • Queries for memories matching the filters
    • Deletes each memory individually by ID
    • This preserves unfiltered memories as intended
  2. Reset Is Unnecessary: The reset() method is designed to clear ALL data, which contradicts the filtering behavior users expect from delete_all(user_id=...). According to the docs, delete_all() should only delete filtered memories (https://docs.mem0.ai/core-concepts/memory-operations/delete). Resetting the whole vector store is unexpected behavior.

  3. Consistent with Method Contract: The method signature and documentation indicate filtered deletion, not a complete reset

Changes Made

  • File: mem0/memory/main.py
  • Line: 1053
  • Change: Commented out # self.vector_store.reset()

How Has This Been Tested?

  • Test Script
def test_delete_all_does_not_call_vector_store_reset(mock_memory_instance):
    """
    Test that delete_all() only deletes specific memories by ID and does not call vector_store.reset().
    
    This test verifies the bug fix where delete_all() was incorrectly calling
    self.vector_store.reset() which wiped the entire collection.
    """
    # Setup: Create mock memories that would be returned by list()
    mock_mem1 = MockMemory("mem_1", {"data": "User likes pizza", "user_id": "user1"})
    mock_mem2 = MockMemory("mem_2", {"data": "User likes pasta", "user_id": "user1"}) 
    mock_mem3 = MockMemory("mem_3", {"data": "Agent learned something", "user_id": "user2"})
    
    # Mock vector_store.list to return only user1's memories
    mock_memory_instance.vector_store.list.return_value = ([mock_mem1, mock_mem2], None)
    
    # Execute: Call delete_all with user_id filter
    with patch('mem0.memory.main.process_telemetry_filters') as mock_telemetry:
        mock_telemetry.return_value = (["user_id"], ["encoded_user1"])
        with patch('mem0.memory.main.capture_event') as mock_capture:
            result = mock_memory_instance.delete_all(user_id="user1")
    
    # Verify: Check that only specific memories were deleted by ID
    assert mock_memory_instance._delete_memory.call_count == 2
    mock_memory_instance._delete_memory.assert_any_call("mem_1")
    mock_memory_instance._delete_memory.assert_any_call("mem_2")
    
    # Critical: Verify that vector_store.reset() was NOT called
    mock_memory_instance.vector_store.reset.assert_not_called()
    
    # Verify the list was called with correct filters
    mock_memory_instance.vector_store.list.assert_called_once_with(filters={"user_id": "user1"})
    
    # Verify return message
    assert result["message"] == "Memories deleted successfully!"

def test_delete_all_preserves_unfiltered_memories():
    """
    Integration test to verify that delete_all() only removes filtered memories
    and leaves other memories intact. This simulates the real-world scenario
    where the bug would cause data loss.
    """
    # This test would ideally use a real vector store, but we'll simulate it with mocks
    with patch('mem0.utils.factory.EmbedderFactory.create'):
        with patch('mem0.utils.factory.VectorStoreFactory.create') as mock_vector_factory:
            with patch('mem0.utils.factory.LlmFactory.create'):
                with patch('mem0.memory.storage.SQLiteManager'):
                    
                    # Setup mock vector store
                    mock_vector_store = MagicMock()
                    mock_vector_factory.return_value = mock_vector_store
                    
                    # Simulate vector store state with memories for different users
                    user1_memories = [
                        MockMemory("mem_1", {"data": "User1 memory 1", "user_id": "user1"}),
                        MockMemory("mem_2", {"data": "User1 memory 2", "user_id": "user1"})
                    ]
                    user2_memories = [
                        MockMemory("mem_3", {"data": "User2 memory 1", "user_id": "user2"}),
                        MockMemory("mem_4", {"data": "User2 memory 2", "user_id": "user2"})
                    ]
                    
                    # Mock list method to return only user1 memories when filtered
                    def mock_list(filters=None):
                        if filters and filters.get("user_id") == "user1":
                            return (user1_memories, None)
                        elif filters and filters.get("user_id") == "user2":
                            return (user2_memories, None)
                        else:
                            # Return all memories (this simulates what reset would affect)
                            return (user1_memories + user2_memories, None)
                    
                    mock_vector_store.list.side_effect = mock_list
                    
                    # Track deletions
                    deleted_ids = []
                    def mock_delete(vector_id):
                        deleted_ids.append(vector_id)
                    
                    mock_vector_store.delete.side_effect = mock_delete
                    
                    # Create memory instance
                    from mem0.memory.main import Memory as MemoryClass
                    memory = MemoryClass()
                    
                    # Mock _delete_memory to track what gets deleted
                    memory._delete_memory = MagicMock(side_effect=lambda mem_id: deleted_ids.append(mem_id))
                    
                    # Execute: Delete all memories for user1
                    with patch('mem0.memory.main.process_telemetry_filters') as mock_telemetry:
                        mock_telemetry.return_value = (["user_id"], ["encoded"])
                        with patch('mem0.memory.main.capture_event'):
                            result = memory.delete_all(user_id="user1")
                    
                    # Verify: Only user1's memories were deleted
                    assert "mem_1" in deleted_ids
                    assert "mem_2" in deleted_ids
                    assert "mem_3" not in deleted_ids  # user2's memories should be preserved
                    assert "mem_4" not in deleted_ids  # user2's memories should be preserved
                    
                    # Verify reset was not called (critical for the bug fix)
                    mock_vector_store.reset.assert_not_called()
                    
                    # Verify list was called with proper filter, not without filter (which reset would imply)
                    mock_vector_store.list.assert_called_with(filters={"user_id": "user1"})
                    
                    assert result["message"] == "Memories deleted successfully!"

Test Results

图片

Before/After Behavior

Before (Buggy)

memory.add("User1 likes pizza", user_id="user1")
memory.add("User2 likes pasta", user_id="user2") 
memory.delete_all(user_id="user1")
# Result: ALL memories deleted (user1 AND user2)

After (Fixed)

memory.add("User1 likes pizza", user_id="user1")
memory.add("User2 likes pasta", user_id="user2")
memory.delete_all(user_id="user1")
# Result: Only user1's memory deleted, user2's memory preserved

Related Issues

This fix resolves the data loss issue where filtered deletions would unexpectedly wipe the entire memory collection.

@CLAassistant
Copy link

CLAassistant commented Nov 12, 2025

CLA assistant check
All committers have signed the CLA.

@peisong13 peisong13 marked this pull request as draft November 12, 2025 11:46
@peisong13 peisong13 force-pushed the fix/memory_delete_all_resets_vector_store branch from dec2e87 to 07c2697 Compare November 12, 2025 11:52
@peisong13 peisong13 marked this pull request as ready for review November 12, 2025 11:54
@peisong13
Copy link
Author

related issue: #3746

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants