{"record":{"id":"5c642f603ad72d0e","repo":"mem0ai/mem0","slug":"key-filter-list-item-at-index-i-must-be-a-dict","errorCode":null,"errorMessage":"{key} filter list item at index {i} must be a dict, got {type(item).__name__}: {item!r}","messagePattern":"(.+?) filter list item at index (.+?) must be a dict, got (.+?): (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mem0/vector_stores/qdrant.py","lineNumber":381,"sourceCode":"        for key, value in filters.items():\n            norm_key = key_map.get(key, key)\n            if norm_key not in normalized:\n                normalized[norm_key] = value\n\n        must = []\n        should = []\n        must_not = []\n\n        for key, value in normalized.items():\n            if key in (\"AND\", \"OR\", \"NOT\"):\n                if not isinstance(value, list):\n                    raise ValueError(\n                        f\"{key} filter value must be a list of filter dicts, \"\n                        f\"got {type(value).__name__}\"\n                    )\n                for i, item in enumerate(value):\n                    if not isinstance(item, dict):\n                        raise ValueError(\n                            f\"{key} filter list item at index {i} must be a dict, \"\n                            f\"got {type(item).__name__}: {item!r}\"\n                        )\n\n            if key == \"AND\":\n                for sub in value:\n                    built = self._create_filter(sub)\n                    if built:\n                        must.append(built)\n            elif key == \"OR\":\n                for sub in value:\n                    built = self._create_filter(sub)\n                    if built:\n                        should.append(built)\n            elif key == \"NOT\":\n                for sub in value:\n                    built = self._create_filter(sub)\n                    if built:","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0/vector_stores/qdrant.py#L363-L399","documentation":"Raised when an AND/OR/NOT list contains an element that is not a dict. Each list element is expected to be a sub-filter object that `_create_filter` can recurse into; strings, numbers, or lists break that contract. The message includes the offending index, type, and repr for fast localization.","triggerScenarios":"`{\"OR\": [\"user_id\"]}` (bare field name), `{\"AND\": [{\"a\": 1}, [\"b\", 2]]}` (nested list), `{\"NOT\": [None]}` from optional fields never populated — any non-dict element inside a logical operator list.","commonSituations":"Programmatic filter assembly appending raw values instead of `{field: {...}}` dicts; JSON deserialization where a caller sent `\"OR\": [\"cond1\"]`; LLM tool output mixing scalar shorthand into condition arrays.","solutions":["Make every element a one-field dict: `{\"OR\": [{\"user_id\": \"u1\"}, {\"agent_id\": \"a9\"}]}`.","If a bare field name appears, it is likely a leftover of intent like equality — rewrite as `{\"field\": value}`.","Filter out None/empty entries when building the list dynamically: `[c for c in conditions if isinstance(c, dict) and c]`."],"exampleFix":"# before\nfilters = {\"OR\": [\"user_id\", {\"agent_id\": \"a9\"}]}\n\n# after\nfilters = {\"OR\": [{\"user_id\": \"u1\"}, {\"agent_id\": \"a9\"}]}\n\n# safe dynamic assembly\nconditions = [c if isinstance(c, dict) else {\"user_id\": c} for c in raw_conditions]\nfilters = {\"OR\": [c for c in conditions if c]}","handlingStrategy":"type-guard","validationCode":"def clean_logic_list(key: str, items):\n    cleaned = [i for i in items if isinstance(i, dict) and i]\n    if len(cleaned) != len(items):\n        raise ValueError(f\"{key} list contains non-dict/empty items: {items!r}\")\n    return cleaned","typeGuard":"def is_valid_logic_list(items) -> bool:\n    return isinstance(items, list) and bool(items) and all(\n        isinstance(i, dict) and i for i in items\n    )","tryCatchPattern":"try:\n    results = memory.search(\"q\", filters=filters)\nexcept ValueError as e:\n    if \"must be a dict\" in str(e):\n        raise BadRequest(f\"Malformed logical filter item: {filters}\") from e\n    raise","preventionTips":["When building condition lists programmatically, append only `{field: condition}` dicts.","Filter out None/empty entries as you assemble, not at query time.","For LLM-generated filters, validate the shape (arrays of objects under logical keys) before use."],"tags":["filters","qdrant","validation","query-syntax","vector-store"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}