mem0ai/mem0 · error · ValueError
Cannot mix range operators ({ops & range_ops}) with non-rang
Error message
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. What it means
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.
Source
Thrown at mem0/vector_stores/qdrant.py:297
"""
if not isinstance(value, dict):
if value == "*":
# Wildcard: match any value. Qdrant has no direct "field exists"
# condition via FieldCondition, so we skip this filter (match all).
return None
if isinstance(value, list):
# List shorthand: {"field": ["a", "b"]} treated as in-operator.
return FieldCondition(key=key, match=MatchAny(any=value))
# Simple equality: {"field": "value"}
return FieldCondition(key=key, match=MatchValue(value=value))
ops = set(value.keys())
range_ops = {"gt", "gte", "lt", "lte"}
non_range_ops = ops - range_ops
if ops & range_ops:
if non_range_ops:
raise ValueError(
f"Cannot mix range operators ({ops & range_ops}) with "
f"non-range operators ({non_range_ops}) for field '{key}'. "
f"Use AND to combine them as separate conditions."
)
range_kwargs = {op: value[op] for op in range_ops if op in value}
if self._is_datetime_range(range_kwargs):
try:
return FieldCondition(key=key, range=DatetimeRange(**range_kwargs))
except (ValueError, TypeError) as e:
raise ValueError(
f"Invalid datetime value in range filter for field '{key}': {e}"
) from e
return FieldCondition(key=key, range=Range(**range_kwargs))
elif "eq" in value:
return FieldCondition(key=key, match=MatchValue(value=value["eq"]))
elif "ne" in value:
return FieldCondition(key=key, match=MatchExcept(**{"except": [value["ne"]]}))
elif "in" in value:View on GitHub (pinned to 001c235229)
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.
Example fix
# before
filters = {"timestamp": {"gte": "2024-01-01", "ne": "2023-12-25"}}
results = memory.search("query", user_id="u1", filters=filters)
# after
filters = {
"AND": [
{"timestamp": {"gte": "2024-01-01"}},
{"timestamp": {"ne": "2023-12-25"}},
]
} Defensive patterns
Strategy: validation
Validate before calling
RANGE_OPS = {"gt", "gte", "lt", "lte"}
SUPPORTED = {"eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "icontains"}
def validate_field_filter(key: str, cond: dict) -> None:
ops = set(cond)
assert ops <= SUPPORTED, f"unsupported ops {ops - SUPPORTED} on {key}"
assert not (ops & RANGE_OPS and ops - RANGE_OPS), f"mixed range/non-range ops on {key}: split with AND" Type guard
from typing import Any
def is_valid_field_condition(value: Any) -> bool:
if not isinstance(value, dict) or not value:
return False
ops = set(value)
range_ops = ops & {"gt", "gte", "lt", "lte"}
return ops <= {"eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "icontains"} and not (range_ops and ops - range_ops) Try / catch
try:
results = memory.search("q", filters=filters)
except ValueError as e:
if "Cannot mix range operators" in str(e):
filters = split_mixed_ops(filters) # rewrite into AND list and retry once
results = memory.search("q", filters=filters)
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unsupported filter operator(s) for field '{key}': {ops}. Sup
- {key} filter value must be a list of filter dicts, got {type
- {key} filter list item at index {i} must be a dict, got {typ
- Invalid datetime value in range filter for field '{key}': {e
- AND filter value must be a list of filter dicts, got ${typeo
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/d0b91b647e3ff93a.
Report an issue: GitHub.