{"record":{"id":"634c77f89080f7d8","repo":"mem0ai/mem0","slug":"unsupported-metadata-filter-operator-operator-634c77","errorCode":null,"errorMessage":"Unsupported metadata filter operator: {operator}","messagePattern":"Unsupported metadata filter operator: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mem0/memory/main.py","lineNumber":1556,"sourceCode":"                # Simple equality: {\"key\": \"value\"}\n                if condition == \"*\":\n                    # Wildcard: match everything for this field (implementation depends on vector store)\n                    return {key: \"*\"}\n                return {key: condition}\n\n            result = {}\n            for operator, value in condition.items():\n                # Map platform operators to universal format that can be translated by each vector store\n                operator_map = {\n                    \"eq\": \"eq\", \"ne\": \"ne\", \"gt\": \"gt\", \"gte\": \"gte\",\n                    \"lt\": \"lt\", \"lte\": \"lte\", \"in\": \"in\", \"nin\": \"nin\",\n                    \"contains\": \"contains\", \"icontains\": \"icontains\"\n                }\n\n                if operator in operator_map:\n                    result.setdefault(key, {})[operator_map[operator]] = value\n                else:\n                    raise ValueError(f\"Unsupported metadata filter operator: {operator}\")\n            return result\n\n        def merge_filters(target: Dict[str, Any], source: Dict[str, Any]) -> None:\n            \"\"\"Merge source into target, deep-merging nested operator dicts for the same key.\"\"\"\n            for key, value in source.items():\n                if key in target and isinstance(target[key], dict) and isinstance(value, dict):\n                    target[key].update(value)\n                else:\n                    target[key] = value\n\n        for key, value in metadata_filters.items():\n            if key == \"AND\":\n                # Logical AND: combine multiple conditions\n                if not isinstance(value, list):\n                    raise ValueError(\"AND operator requires a list of conditions\")\n                for condition in value:\n                    for sub_key, sub_value in condition.items():\n                        merge_filters(processed_filters, process_condition(sub_key, sub_value))","sourceCodeStart":1538,"sourceCodeEnd":1574,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0/memory/main.py#L1538-L1574","documentation":"Raised inside _process_metadata_filters (used by search()/get_all() when advanced operators are detected) when a condition dict uses an operator key outside the supported set. Supported operators are exactly: eq, ne, gt, gte, lt, lte, in, nin, contains, icontains. Anything else — 'gteq', 'between', 'regex', 'exists', '$gte' with a dollar prefix, or a typo — raises this ValueError naming the bad operator.","triggerScenarios":"filters={'user_id':'u1','score':{'$gte':0.5}} using Mongo-style $-prefixed operators; {'ts':{'between':[1,2]}}; {'name':{'regex':'^a'}}; typo 'gte ' with whitespace or 'GTE' uppercase (matching is case-sensitive); copying filter syntax from Pinecone/Qdrant/Mongo docs into mem0 filters.","commonSituations":"Developers fluent in MongoDB or Elasticsearch filter syntax assuming the same grammar; LLM-generated filter dicts inventing operators; case mismatches from config-driven filter construction.","solutions":["Replace the operator with one of the ten supported names, lowercase, no $ prefix (e.g. $gte -> gte, $in -> in).","Express 'between' as two conditions merged under AND: {'AND': [{'ts': {'gte': 1}}, {'ts': {'lte': 2}}]}.","Validate filter dicts against the supported set before calling search/get_all (see validation code in the defense section).","For regex-like matching, use contains/icontains for substring and case-insensitive substring respectively."],"exampleFix":"# before\nfilters = {\"user_id\": \"u1\", \"score\": {\"$gte\": 0.5}}\n\n# after\nfilters = {\"user_id\": \"u1\", \"score\": {\"gte\": 0.5}}","handlingStrategy":"validation","validationCode":"SUPPORTED_OPS = {\"eq\", \"ne\", \"gt\", \"gte\", \"lt\", \"lte\", \"in\", \"nin\", \"contains\", \"icontains\"}\nLOGICAL_OPS = {\"AND\", \"OR\", \"NOT\"}\n\ndef validate_filters(filters):\n    for key, value in filters.items():\n        if key in LOGICAL_OPS:\n            if not isinstance(value, list):\n                raise ValueError(f\"{key} must be a list\")\n            for cond in value:\n                validate_filters(cond)\n        elif isinstance(value, dict):\n            bad = set(value) - SUPPORTED_OPS\n            if bad:\n                raise ValueError(f\"unsupported operators: {bad}; supported: {sorted(SUPPORTED_OPS)}\")","typeGuard":"def is_supported_operator(op: str) -> bool:\n    return op in {\"eq\", \"ne\", \"gt\", \"gte\", \"lt\", \"lte\", \"in\", \"nin\", \"contains\", \"icontains\"}","tryCatchPattern":null,"preventionTips":["Use lowercase operators without $ prefixes — this is not Mongo syntax.","Express ranges as {'AND': [{'k': {'gte': lo}}, {'k': {'lte': hi}}]}.","Validate LLM-generated filter dicts against the supported set before passing them in.","Use contains/icontains for substring matching; there is no regex operator."],"tags":["validation","filters","operators","search"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}