MemPalace/mempalace · error · UnsupportedFilterError

operator {key!r} not supported by milvus

Error message

operator {key!r} not supported by milvus

What it means

Raised in _translate_clause() when a where-clause key starts with '$' but is neither '$and' nor '$or'. At the clause level only those two logical operators exist; all $-prefixed field names or misplaced operators ($in, $contains at top level, $nor, etc.) hit this branch and raise UnsupportedFilterError. This guards against typos and against putting field-level operators at the wrong nesting depth.

Source

Thrown at mempalace/backends/milvus.py:162

def _translate_clause(clause: dict) -> str:
    if not isinstance(clause, dict):
        raise UnsupportedFilterError(f"where clause must be a dict, got {type(clause).__name__}")
    if not clause:
        return ""
    parts = []
    for key, value in clause.items():
        if key == "$and":
            if not isinstance(value, list) or not value:
                raise UnsupportedFilterError("$and requires a non-empty list of clauses")
            nested = [_translate_clause(item) for item in value]
            parts.append("(" + " and ".join(part for part in nested if part) + ")")
        elif key == "$or":
            if not isinstance(value, list) or not value:
                raise UnsupportedFilterError("$or requires a non-empty list of clauses")
            nested = [_translate_clause(item) for item in value]
            parts.append("(" + " or ".join(part for part in nested if part) + ")")
        elif key.startswith("$"):
            raise UnsupportedFilterError(f"operator {key!r} not supported by milvus")
        else:
            parts.append(_translate_field(key, value))
    return " and ".join(part for part in parts if part)


def translate_where(where: Optional[dict]) -> str:
    """Translate the portable metadata where DSL into a Milvus filter string."""
    if not where:
        return ""
    return _translate_clause(where)


def translate_where_document(where_document: Optional[dict]) -> str:
    """Translate the portable document filter subset into a Milvus filter."""
    if not where_document:
        return ""
    if not isinstance(where_document, dict):
        raise UnsupportedFilterError("where_document must be a dict")

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. Move operators inside a field dict: {"wing": {"$in": [...]}} not {"$in": [...]}
  2. Rename metadata keys that start with '$' — _field_name would reject them anyway
  3. Rewrite $not via $ne/$nin on the field

Example fix

// before
where = {"$in": ["a", "b"]}

// after
where = {"wing": {"$in": ["a", "b"]}}
Defensive patterns

Strategy: validation

Validate before calling

CLAUSE_LEVEL_OPS = {"$and", "$or"}

def no_misplaced_operators(where: dict) -> bool:
    return all(k in CLAUSE_LEVEL_OPS or not k.startswith("$") for k in where)

Try / catch

from mempalace.backends.base import UnsupportedFilterError
try:
    collection.query(query_texts=[q], where=where, n_results=k)
except UnsupportedFilterError as e:
    if "not supported by milvus" in str(e):
        raise ValueError(f"move operator {e} inside a field's condition dict") from e
    raise

Prevention

When it happens

Trigger: where={"$in": [...]}, where={"$not": {...}}, or a metadata field literally named "$price" used as a clause key. Distinct from error 24, which fires for unknown operators nested inside a field's condition dict.

Common situations: Misplaced operators after refactoring filter-building code; Mongo $nor/$not habits; metadata keys that begin with a dollar sign.

Related errors


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