MemPalace/mempalace · error · UnsupportedFilterError
$in requires a non-empty list for {field!r}
Error message
$in requires a non-empty list for {field!r} What it means
Raised in _translate_field() when the $in operator's operand is not a list or is an empty list. Milvus 'in [..]' over an empty set is invalid/meaningless, so the translator refuses it up front rather than sending a malformed filter. It is an UnsupportedFilterError thrown synchronously during where-clause translation, before any network call.
Source
Thrown at mempalace/backends/milvus.py:120
def _field_name(name: str) -> str:
if not isinstance(name, str) or not _FIELD_RE.match(name):
raise UnsupportedFilterError(f"Milvus filter field {name!r} is not a safe identifier")
return name
def _translate_field(field: str, expected: Any) -> str:
field = _field_name(field)
if isinstance(expected, dict):
parts = []
for op, operand in expected.items():
if op == "$eq":
parts.append(f"{field} == {_quote_value(operand)}")
elif op == "$ne":
parts.append(f"{field} != {_quote_value(operand)}")
elif op == "$in":
if not isinstance(operand, list) or not operand:
raise UnsupportedFilterError(f"$in requires a non-empty list for {field!r}")
items = ", ".join(_quote_value(item) for item in operand)
parts.append(f"{field} in [{items}]")
elif op == "$nin":
if not isinstance(operand, list) or not operand:
raise UnsupportedFilterError(f"$nin requires a non-empty list for {field!r}")
items = ", ".join(_quote_value(item) for item in operand)
parts.append(f"{field} not in [{items}]")
elif op == "$gt":
parts.append(f"{field} > {_quote_value(operand)}")
elif op == "$gte":
parts.append(f"{field} >= {_quote_value(operand)}")
elif op == "$lt":
parts.append(f"{field} < {_quote_value(operand)}")
elif op == "$lte":
parts.append(f"{field} <= {_quote_value(operand)}")
elif op == "$contains":
parts.append(f"{field} like {_like_value(operand)}")
else:View on GitHub (pinned to 06cb6987f0)
Solutions
- Skip the query (return empty results) when the $in list would be empty — it can match nothing anyway
- Guard before calling: if isinstance(v, dict) and '$in' in v and not v['$in']: return []
- Ensure the operand is a real list: wrap single values as [value]
Example fix
// before
where = {"wing": {"$in": found_wings}} # found_wings == []
collection.query(query_texts=[q], where=where)
// after
if not found_wings:
return []
collection.query(query_texts=[q], where={"wing": {"$in": found_wings}}) Defensive patterns
Strategy: validation
Validate before calling
def is_valid_in_filter(where: dict) -> bool:
for v in where.values():
if isinstance(v, dict) and "$in" in v:
operand = v["$in"]
if not isinstance(operand, list) or not operand:
return False
return True Try / catch
from mempalace.backends.base import UnsupportedFilterError
try:
collection.query(query_texts=[q], where=where, n_results=k)
except UnsupportedFilterError as e:
if "$in" in str(e):
return [] # empty candidate set matches nothing
raise Prevention
- Short-circuit queries with empty $in lists to empty results
- In filter builders, coerce scalar operands to [value]
- Test builder output against the supported-operator contract
When it happens
Trigger: where={"wing": {"$in": []}}, where={"wing": {"$in": "people"}} (string instead of list), or a filter built from an empty computed list, e.g. {"tag": {"$in": matching_tags}} where matching_tags ends up empty.
Common situations: Dynamically computed candidate sets (e.g. entity names found by a detector) that legitimately come back empty; the caller intends 'no matches' but the backend rejects the clause.
Related errors
- Milvus filter field {name!r} is not a safe identifier
- $nin requires a non-empty list for {field!r}
- Milvus filters do not support null comparisons
- operator {op!r} not supported by milvus
- where clause must be a dict, got {type(clause).__name__}
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/8c10bb34f0bf0847.
Report an issue: GitHub.