MemPalace/mempalace · error · UnsupportedFilterError

operator {op!r} not supported by qdrant

Error message

operator {op!r} not supported by qdrant

What it means

_compare implements the local-filter fallback ($gt, $gte, $lt, $lte and equality/contains semantics). If it is ever called with an operator outside that set it raises UnsupportedFilterError as a defensive assertion — normally unreachable because _validate_where rejects unknown operators first. Hitting it means an operator slipped past validation (e.g. a newly added comparator or an operator reachable only through _matches_where paths).

Source

Thrown at mempalace/backends/qdrant.py:169

        return actual != expected
    if op == "$in":
        return actual in (expected or [])
    if op == "$nin":
        return actual not in (expected or [])
    if op == "$contains":
        return str(expected) in str(actual or "")
    try:
        if op == "$gt":
            return actual > expected
        if op == "$gte":
            return actual >= expected
        if op == "$lt":
            return actual < expected
        if op == "$lte":
            return actual <= expected
    except TypeError:
        return False
    raise UnsupportedFilterError(f"operator {op!r} not supported by qdrant")


def _matches_where(meta: dict, where: Optional[dict]) -> bool:
    if not where:
        return True
    if not isinstance(where, dict):
        return False
    for key, expected in where.items():
        if key == "$and":
            if not all(_matches_where(meta, clause) for clause in expected or []):
                return False
            continue
        if key == "$or":
            if not any(_matches_where(meta, clause) for clause in expected or []):
                return False
            continue
        if key.startswith("$"):
            raise UnsupportedFilterError(f"operator {key!r} not supported by qdrant")

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. If you maintain the library: add a matching branch in _compare for any operator you add to _SUPPORTED_OPERATORS (and a test).
  2. As a caller: stick to the documented operator set and rewrite exotic filters.
  3. Report the specific operator that triggered it — this path indicates an internal inconsistency.

Example fix

# before
# _SUPPORTED_OPERATORS extended with "$ne" but _compare has no branch:
_matches_where(meta, {"status": {"$ne": "archived"}})

# after
# add to _compare:
#   if op == "$ne":
#       return actual != expected
Defensive patterns

Strategy: validation

Validate before calling

# callers: pre-restrict operators to the set _compare implements
ALLOWED_COMPARE = {"$eq", "$gt", "$gte", "$lt", "$lte"}
def safe_compare(actual, op, expected):
    if op not in ALLOWED_COMPARE:
        raise ValueError(f"unsupported comparator {op}")
    return _compare(actual, op, expected)

Type guard

def is_comparable_op(op: str) -> bool:
    return op in {"$eq", "$gt", "$gte", "$lt", "$lte"}

Try / catch

try:
    _compare(actual, op, expected)
except UnsupportedFilterError:
    logger.exception("internal filter mismatch: operator %r lacks a compare branch", op)
    raise

Prevention

When it happens

Trigger: Calling _compare(meta_value, "$regex", "x") directly, or a filter operator that exists in _SUPPORTED_OPERATORS but has no branch in _compare.

Common situations: Extending _SUPPORTED_OPERATORS without adding a _compare branch; library upgrades that add pushdown operators without local-comparison counterparts.

Related errors


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/b1b0480fabdf3124. Report an issue: GitHub.