{"record":{"id":"d0b91b647e3ff93a","repo":"mem0ai/mem0","slug":"cannot-mix-range-operators-ops-range-ops-wit","errorCode":null,"errorMessage":"Cannot mix range operators ({ops & range_ops}) with non-range operators ({non_range_ops}) for field '{key}'. Use AND to combine them as separate conditions.","messagePattern":"Cannot mix range operators \\((.+?)\\) with non-range operators \\((.+?)\\) for field '(.+?)'\\. Use AND to combine them as separate conditions\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mem0/vector_stores/qdrant.py","lineNumber":297,"sourceCode":"        \"\"\"\n        if not isinstance(value, dict):\n            if value == \"*\":\n                # Wildcard: match any value. Qdrant has no direct \"field exists\"\n                # condition via FieldCondition, so we skip this filter (match all).\n                return None\n            if isinstance(value, list):\n                # List shorthand: {\"field\": [\"a\", \"b\"]} treated as in-operator.\n                return FieldCondition(key=key, match=MatchAny(any=value))\n            # Simple equality: {\"field\": \"value\"}\n            return FieldCondition(key=key, match=MatchValue(value=value))\n\n        ops = set(value.keys())\n        range_ops = {\"gt\", \"gte\", \"lt\", \"lte\"}\n        non_range_ops = ops - range_ops\n\n        if ops & range_ops:\n            if non_range_ops:\n                raise ValueError(\n                    f\"Cannot mix range operators ({ops & range_ops}) with \"\n                    f\"non-range operators ({non_range_ops}) for field '{key}'. \"\n                    f\"Use AND to combine them as separate conditions.\"\n                )\n            range_kwargs = {op: value[op] for op in range_ops if op in value}\n            if self._is_datetime_range(range_kwargs):\n                try:\n                    return FieldCondition(key=key, range=DatetimeRange(**range_kwargs))\n                except (ValueError, TypeError) as e:\n                    raise ValueError(\n                        f\"Invalid datetime value in range filter for field '{key}': {e}\"\n                    ) from e\n            return FieldCondition(key=key, range=Range(**range_kwargs))\n        elif \"eq\" in value:\n            return FieldCondition(key=key, match=MatchValue(value=value[\"eq\"]))\n        elif \"ne\" in value:\n            return FieldCondition(key=key, match=MatchExcept(**{\"except\": [value[\"ne\"]]}))\n        elif \"in\" in value:","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0/vector_stores/qdrant.py#L279-L315","documentation":"Raised by Qdrant's filter translator when a single field dictionary mixes comparison operators (gt/gte/lt/lte) with other operators (eq, ne, in, nin, contains, icontains). Qdrant's FieldCondition can hold either a Range or a Match, not both, so one condition object cannot express `{\"gte\": 5, \"ne\": 7}`. The message tells you to split it into separate conditions joined with AND.","triggerScenarios":"Calling search/get with filters like `{\"user_id\": {\"gte\": 100, \"eq\": 42}}`, `{\"timestamp\": {\"lt\": \"2024-01-01\", \"ne\": \"2023-12-25\"}}`, or `{\"score\": {\"gt\": 0, \"in\": [1, 2]}}` — any dict under one field key containing both a range op and any non-range op.","commonSituations":"Porting Mongo-style queries (`{\"$gte\": ..., \"$ne\": ...}` on one field) verbatim to mem0's filter syntax; LLM-generated filter JSON collapsing multiple constraints onto one field; incrementally adding a `ne` exclusion to an existing range filter without restructuring.","solutions":["Split the field into two entries under an AND list: `{\"AND\": [{\"user_id\": {\"gte\": 100}}, {\"user_id\": {\"ne\": 42}}]}`.","If the mix was accidental (e.g. eq plus gte on the same value), drop the redundant operator and keep only one.","For exact-match-plus-range patterns, remember eq and range are different FieldConditions in Qdrant — model them as separate AND clauses by design."],"exampleFix":"# before\nfilters = {\"timestamp\": {\"gte\": \"2024-01-01\", \"ne\": \"2023-12-25\"}}\nresults = memory.search(\"query\", user_id=\"u1\", filters=filters)\n\n# after\nfilters = {\n    \"AND\": [\n        {\"timestamp\": {\"gte\": \"2024-01-01\"}},\n        {\"timestamp\": {\"ne\": \"2023-12-25\"}},\n    ]\n}","handlingStrategy":"validation","validationCode":"RANGE_OPS = {\"gt\", \"gte\", \"lt\", \"lte\"}\nSUPPORTED = {\"eq\", \"ne\", \"gt\", \"gte\", \"lt\", \"lte\", \"in\", \"nin\", \"contains\", \"icontains\"}\n\ndef validate_field_filter(key: str, cond: dict) -> None:\n    ops = set(cond)\n    assert ops <= SUPPORTED, f\"unsupported ops {ops - SUPPORTED} on {key}\"\n    assert not (ops & RANGE_OPS and ops - RANGE_OPS), f\"mixed range/non-range ops on {key}: split with AND\"","typeGuard":"from typing import Any\n\ndef is_valid_field_condition(value: Any) -> bool:\n    if not isinstance(value, dict) or not value:\n        return False\n    ops = set(value)\n    range_ops = ops & {\"gt\", \"gte\", \"lt\", \"lte\"}\n    return ops <= {\"eq\", \"ne\", \"gt\", \"gte\", \"lt\", \"lte\", \"in\", \"nin\", \"contains\", \"icontains\"} and not (range_ops and ops - range_ops)","tryCatchPattern":"try:\n    results = memory.search(\"q\", filters=filters)\nexcept ValueError as e:\n    if \"Cannot mix range operators\" in str(e):\n        filters = split_mixed_ops(filters)  # rewrite into AND list and retry once\n        results = memory.search(\"q\", filters=filters)\n    else:\n        raise","preventionTips":["Keep one operator per field dict; combine extra constraints as separate entries in an AND list.","Validate externally sourced (user/LLM) filter JSON with a schema (Pydantic/jsonschema) before passing it to search.","Write a unit test over your filter-building helpers asserting no field dict mixes range and match ops."],"tags":["filters","qdrant","validation","vector-store","query-syntax"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}