MemPalace/mempalace · error · UnsupportedFilterError
$nin requires a non-empty list for {field!r}
Error message
$nin requires a non-empty list for {field!r} What it means
Raised in _translate_field() when the $nin operand is not a list or is empty — the mirror of the $in check. An empty 'not in' set is degenerate in Milvus filter syntax, so translation fails with UnsupportedFilterError before any request is made. Only $nin and $in have the list requirement; $eq/$ne accept scalars.
Source
Thrown at mempalace/backends/milvus.py:125
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:
raise UnsupportedFilterError(f"operator {op!r} not supported by milvus")
return " and ".join(parts)
return f"{field} == {_quote_value(expected)}"
View on GitHub (pinned to 06cb6987f0)
Solutions
- Drop the $nin clause entirely when the exclusion list is empty — 'not in []' is equivalent to no filter
- Wrap scalars into a list: {"$nin": [value]}
- Guard with a builder that strips empty $nin keys before calling query
Example fix
// before
where = {"wing": {"$nin": excluded}} # excluded == []
collection.query(query_texts=[q], where=where)
// after
where = {"wing": {"$nin": excluded}} if excluded else None
collection.query(query_texts=[q], where=where) Defensive patterns
Strategy: validation
Validate before calling
def drop_empty_nin(where):
if not where:
return None
out = {}
for k, v in where.items():
if isinstance(v, dict):
if "$nin" in v and (not isinstance(v["$nin"], list) or not v["$nin"]):
v = {op: val for op, val in v.items() if op != "$nin"}
if not v:
continue
out[k] = v
return out or None Try / catch
from mempalace.backends.base import UnsupportedFilterError
try:
collection.query(query_texts=[q], where=where, n_results=k)
except UnsupportedFilterError as e:
if "$nin" in str(e):
where2 = {k: {op: val for op, val in v.items() if op != "$nin"} if isinstance(v, dict) else v for k, v in where.items()}
return collection.query(query_texts=[q], where=where2, n_results=k)
raise Prevention
- Treat empty exclusion lists as 'no filter'
- Wrap scalars in lists when building $nin
- Assert all $in/$nin operands are non-empty lists in debug builds
When it happens
Trigger: where={"wing": {"$nin": []}}, where={"room": {"$nin": "2024-01-01"}} (scalar instead of list), or exclusion lists computed at runtime that end up empty.
Common situations: 'Exclude these wings/rooms' filters where the exclusion set is data-driven and occasionally empty; porting Mongo queries where $nin: [] is accepted as 'match all'.
Related errors
- Milvus filter field {name!r} is not a safe identifier
- $in 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/2a5c543b8a7324cf.
Report an issue: GitHub.