diff --git a/mem0/vector_stores/pgvector.py b/mem0/vector_stores/pgvector.py index 454313886..f55435572 100644 --- a/mem0/vector_stores/pgvector.py +++ b/mem0/vector_stores/pgvector.py @@ -89,6 +89,10 @@ def _build_filter_conditions(filters): raise ValueError(f"Unsupported filter operator: {op}") template, is_numeric = OPERATOR_SQL_MAP[op] if op in ("in", "nin"): + if not isinstance(op_value, list): + raise ValueError( + f"Filter operator {op!r} for key {key!r} requires a list value, got {type(op_value).__name__}" + ) str_list = [str(v) for v in op_value] conditions.append(template) params.extend([key, str_list]) diff --git a/tests/vector_stores/test_pgvector.py b/tests/vector_stores/test_pgvector.py index f4a3351af..308246f1b 100644 --- a/tests/vector_stores/test_pgvector.py +++ b/tests/vector_stores/test_pgvector.py @@ -2508,3 +2508,20 @@ class TestBuildFilterConditions(unittest.TestCase): def test_numeric_scalar_becomes_string(self): conditions, params = _build_filter_conditions({"priority": 42}) self.assertEqual(params, ["priority", "42"]) + + def test_in_rejects_string_value(self): + """Passing a string to 'in' would iterate characters and produce a misleading ANY() clause.""" + with self.assertRaises(ValueError, msg="Expected ValueError for non-list 'in' value"): + _build_filter_conditions({"user_id": {"in": "alice"}}) + + def test_in_rejects_dict_value(self): + with self.assertRaises(ValueError): + _build_filter_conditions({"user_id": {"in": {"$gt": 0}}}) + + def test_nin_rejects_string_value(self): + with self.assertRaises(ValueError): + _build_filter_conditions({"user_id": {"nin": "alice"}}) + + def test_in_accepts_list_value(self): + conditions, params = _build_filter_conditions({"user_id": {"in": ["alice", "bob"]}}) + self.assertEqual(params, ["user_id", ["alice", "bob"]])