MemPalace/mempalace · error · UnsupportedFilterError
operator {key!r} not supported by qdrant
Error message
operator {key!r} not supported by qdrant What it means
The qdrant backend supports only a fixed set of filter operators (_SUPPORTED_OPERATORS). _validate_where walks the whole filter tree, and any $-prefixed key outside that set raises UnsupportedFilterError before any request hits Qdrant — so unsupported ChromaDB-style filters fail fast with a precise message instead of behaving inconsistently.
Source
Thrown at mempalace/backends/qdrant.py:132
for term, freq in tf.items():
num = freq * (k1 + 1)
den = freq + k1 * (1 - b + b * dl / avgdl)
score += float(idf[term]) * num / den
scores.append(score)
return scores
def _validate_where(where: Optional[dict]) -> None:
if not where:
return
stack = [where]
while stack:
node = stack.pop()
if not isinstance(node, dict):
continue
for key, value in node.items():
if key.startswith("$") and key not in _SUPPORTED_OPERATORS:
raise UnsupportedFilterError(f"operator {key!r} not supported by qdrant")
if isinstance(value, dict):
stack.append(value)
elif isinstance(value, list):
stack.extend(item for item in value if isinstance(item, dict))
def _coerce_comparable(value: Any):
if isinstance(value, bool):
return int(value)
return value
def _compare(actual: Any, op: str, expected: Any) -> bool:
actual = _coerce_comparable(actual)
expected = _coerce_comparable(expected)
if op == "$eq":
return actual == expected
if op == "$ne":View on GitHub (pinned to 06cb6987f0)
Solutions
- Rewrite the filter using only the operators listed in qdrant.py's _SUPPORTED_OPERATORS (equality, $and/$or, common comparators).
- Fetch with a broader pushdown filter and post-filter rows locally via _matches_where-compatible logic.
- Centralize filter construction per backend rather than sharing one Mongo-dialect builder.
Example fix
# before
col.get(where={"status": {"$ne": "archived"}})
# after
col.get(where={"$or": [{"status": "active"}, {"status": "pending"}]}) Defensive patterns
Strategy: validation
Validate before calling
SUPPORTED = {"$and", "$or", "$eq", "$ne", "$gt", "$gte", "$lt", "$lte", "$in", "$contains"}
def validate_filter(where):
stack = [where]
while stack:
node = stack.pop()
if not isinstance(node, dict):
continue
for k in node:
if k.startswith("$") and k not in SUPPORTED:
raise ValueError(f"filter uses unsupported operator {k}")
v = node[k]
if isinstance(v, dict): stack.append(v)
elif isinstance(v, list): stack.extend(x for x in v if isinstance(x, dict))
return where Type guard
def is_supported_filter(where: dict, supported: set) -> bool:
stack = [where]
while stack:
node = stack.pop()
if not isinstance(node, dict): continue
for k, v in node.items():
if k.startswith("$") and k not in supported: return False
if isinstance(v, dict): stack.append(v)
elif isinstance(v, list): stack.extend(x for x in v if isinstance(x, dict))
return True Try / catch
try:
col.get(where=filters)
except UnsupportedFilterError as e:
logger.warning("filter rejected by qdrant, simplifying: %s", e)
col.get(where=simplify(filters)) Prevention
- Keep one whitelist of filter operators per backend in your codebase.
- Validate filters client-side before sending them to any backend.
- Avoid niche Mongo operators ($nin, $nor, $regex) in portable filter code.
When it happens
Trigger: Passing where={"meta": {"$nin": [1,2]}} or operators like $ne/$not (whichever are absent from _SUPPORTED_OPERATORS) to get/query/count on a qdrant collection.
Common situations: Copying filters from ChromaDB code; a shared filter-builder utility emitting the full Mongo-style operator set against multiple backends.
Related errors
- operator {op!r} not supported by qdrant
- where_document operator {key!r} not supported
- facet_counts does not support local-only filters
- facet_counts does not support local-only filters
- pgvector does not support maintenance kind {kind!r}
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/cc705445329f35f3.
Report an issue: GitHub.