From 770ce97bd9333d289d12076d5e546be265b02827 Mon Sep 17 00:00:00 2001 From: Abhay Singh Date: Fri, 10 Jul 2026 20:00:33 +0530 Subject: [PATCH] fix(core): coerce non-string entity ids instead of crashing on .strip() (#6206) --- mem0/memory/main.py | 8 +++++++- tests/test_main.py | 24 +++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/mem0/memory/main.py b/mem0/memory/main.py index b2d27f32c..fedabbe5b 100644 --- a/mem0/memory/main.py +++ b/mem0/memory/main.py @@ -145,9 +145,10 @@ def _reject_top_level_entity_params(kwargs: Dict[str, Any], method_name: str) -> ) -def _validate_and_trim_entity_id(value: Optional[str], name: str) -> Optional[str]: +def _validate_and_trim_entity_id(value: Optional[Any], name: str) -> Optional[str]: """ Validates and normalizes an entity ID. + - Coerces non-string values (e.g. integer ids) to str - Trims leading/trailing whitespace - Rejects empty or whitespace-only strings - Rejects strings containing internal whitespace @@ -164,6 +165,11 @@ def _validate_and_trim_entity_id(value: Optional[str], name: str) -> Optional[st """ if value is None: return None + # Callers commonly pass integer ids (e.g. a database primary key). Coerce + # to str at this single validation point so scoping stays consistent across + # add/search/get_all/delete_all instead of crashing on `.strip()`. + if not isinstance(value, str): + value = str(value) trimmed = value.strip() if trimmed == "": raise ValueError( diff --git a/tests/test_main.py b/tests/test_main.py index 6b0638355..7892433b9 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -5,7 +5,7 @@ from unittest.mock import Mock, patch import pytest from mem0.configs.base import MemoryConfig -from mem0.memory.main import Memory +from mem0.memory.main import Memory, _validate_and_trim_entity_id @pytest.fixture(autouse=True) @@ -433,6 +433,28 @@ class TestEntityIdValidation: memory_instance.vector_store.list.assert_called_once_with(filters={"user_id": "alice"}) + def test_validate_coerces_non_string_entity_id(self): + """Integer (and other non-string) ids are coerced to str, not crashed on.""" + assert _validate_and_trim_entity_id(42, "user_id") == "42" + assert _validate_and_trim_entity_id(0, "user_id") == "0" + + def test_delete_all_coerces_integer_user_id_before_list(self, memory_instance): + """delete_all should accept an integer user_id and scope by its str form.""" + memory_instance.vector_store.list = Mock(return_value=([], None)) + + memory_instance.delete_all(user_id=42) + + memory_instance.vector_store.list.assert_called_once_with(filters={"user_id": "42"}) + + def test_get_all_coerces_integer_user_id(self, memory_instance): + """get_all should accept an integer user_id in filters and scope by its str form.""" + memory_instance.vector_store.list = Mock(return_value=([], None)) + + memory_instance.get_all(filters={"user_id": 42}) + + _, kwargs = memory_instance.vector_store.list.call_args + assert kwargs["filters"]["user_id"] == "42" + class TestSearchParamValidation: """Tests for search parameter validation (threshold and top_k)."""