MemPalace/mempalace · error · UnsupportedFilterError

$or requires a non-empty list of clauses

Error message

$or requires a non-empty list of clauses

What it means

Raised in _translate_clause() when a $or key's value is not a non-empty list. $or must be followed by a list of clause dicts that get parenthesized and joined with 'or'; a dict operand, scalar, or empty list is rejected with UnsupportedFilterError during translation. Identical contract to $and but for disjunction.

Source

Thrown at mempalace/backends/milvus.py:158

        return " and ".join(parts)
    return f"{field} == {_quote_value(expected)}"


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."""

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. Use list form: {"$or": [{"wing": "a"}, {"wing": "b"}]}
  2. For same-field alternatives prefer $in: {"wing": {"$in": ["a", "b"]}}
  3. Skip the $or clause if the alternative list is empty

Example fix

// before
where = {"$or": [{"wing": "a"}, {"wing": "b"}, ]} if names else {"$or": []}

// after
where = {"wing": {"$in": names}} if names else None
Defensive patterns

Strategy: validation

Validate before calling

def build_or(clauses):
    clauses = [c for c in clauses if c]
    if not clauses:
        return None
    if len(clauses) == 1:
        return clauses[0]
    return {"$or": clauses}

Try / catch

from mempalace.backends.base import UnsupportedFilterError
try:
    collection.query(query_texts=[q], where=where, n_results=k)
except UnsupportedFilterError as e:
    if "$or" in str(e):
        raise ValueError("$or must be a non-empty list of clause dicts") from e
    raise

Prevention

When it happens

Trigger: where={"$or": {"wing": "a", "wing": "b"}} (dict, and also lossy), {"$or": []}, or {"$or": "x"}.

Common situations: Building 'any of these wings' filters from a name list; forgetting to wrap single alternatives in list+dict form.

Related errors


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