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

  1. Split the field into two entries under an AND list: `{"AND": [{"user_id": {"gte": 100}}, {"user_id": {"ne": 42}}]}`.
  2. If the mix was accidental (e.g. eq plus gte on the same value), drop the redundant operator and keep only one.
  3. 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

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


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/d0b91b647e3ff93a. Report an issue: GitHub.