{"record":{"id":"7437be373ce49298","repo":"MemPalace/mempalace","slug":"and-requires-a-non-empty-list-of-clauses","errorCode":null,"errorMessage":"$and requires a non-empty list of clauses","messagePattern":"\\$and requires a non-empty list of clauses","errorType":"exception","errorClass":"UnsupportedFilterError","httpStatus":null,"severity":"error","filePath":"mempalace/backends/milvus.py","lineNumber":153,"sourceCode":"                parts.append(f\"{field} <= {_quote_value(operand)}\")\n            elif op == \"$contains\":\n                parts.append(f\"{field} like {_like_value(operand)}\")\n            else:\n                raise UnsupportedFilterError(f\"operator {op!r} not supported by milvus\")\n        return \" and \".join(parts)\n    return f\"{field} == {_quote_value(expected)}\"\n\n\ndef _translate_clause(clause: dict) -> str:\n    if not isinstance(clause, dict):\n        raise UnsupportedFilterError(f\"where clause must be a dict, got {type(clause).__name__}\")\n    if not clause:\n        return \"\"\n    parts = []\n    for key, value in clause.items():\n        if key == \"$and\":\n            if not isinstance(value, list) or not value:\n                raise UnsupportedFilterError(\"$and requires a non-empty list of clauses\")\n            nested = [_translate_clause(item) for item in value]\n            parts.append(\"(\" + \" and \".join(part for part in nested if part) + \")\")\n        elif key == \"$or\":\n            if not isinstance(value, list) or not value:\n                raise UnsupportedFilterError(\"$or requires a non-empty list of clauses\")\n            nested = [_translate_clause(item) for item in value]\n            parts.append(\"(\" + \" or \".join(part for part in nested if part) + \")\")\n        elif key.startswith(\"$\"):\n            raise UnsupportedFilterError(f\"operator {key!r} not supported by milvus\")\n        else:\n            parts.append(_translate_field(key, value))\n    return \" and \".join(part for part in parts if part)\n\n\ndef translate_where(where: Optional[dict]) -> str:\n    \"\"\"Translate the portable metadata where DSL into a Milvus filter string.\"\"\"\n    if not where:\n        return \"\"","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/backends/milvus.py#L135-L171","documentation":"Raised in _translate_clause() when a top-level (or nested) $and key has a value that is not a non-empty list. $and composes sub-clauses, so its operand must be a list of at least one clause dict; anything else (dict, string, empty list) is an UnsupportedFilterError. Each sub-clause is itself recursively translated.","triggerScenarios":"where={\"$and\": {\"a\": 1, \"b\": 2}} (dict instead of list), {\"$and\": []}, or {\"$and\": [\"a=1\"]} where the element later fails the dict check (error 25).","commonSituations":"Assuming $and takes a dict of conditions (some DSLs allow both shapes); programmatically building conjunctions that collapse to zero when a filter list is empty.","solutions":["Use list form: {\"$and\": [{\"a\": 1}, {\"b\": 2}]}","Remember sibling keys in one dict are already ANDed — {\"a\": 1, \"b\": 2} needs no $and","When the operand list may be empty, skip $and and pass None/{} as the where clause"],"exampleFix":"// before\nwhere = {\"$and\": {\"wing\": \"alice\", \"room\": \"2024\"}}\n\n// after\nwhere = {\"$and\": [{\"wing\": \"alice\"}, {\"room\": \"2024\"}]}\n# or simply\nwhere = {\"wing\": \"alice\", \"room\": \"2024\"}","handlingStrategy":"validation","validationCode":"def build_and(clauses):\n    clauses = [c for c in clauses if c]\n    if not clauses:\n        return None\n    if len(clauses) == 1:\n        return clauses[0]\n    return {\"$and\": clauses}  # every element a dict, list never empty","typeGuard":null,"tryCatchPattern":"from mempalace.backends.base import UnsupportedFilterError\ntry:\n    collection.query(query_texts=[q], where=where, n_results=k)\nexcept UnsupportedFilterError as e:\n    if \"$and\" in str(e):\n        raise ValueError(\"$and must be a non-empty list of clause dicts\") from e\n    raise","preventionTips":["Prefer flat sibling keys over $and — they are ANDed implicitly","Use a build_and() helper that never emits empty lists","Remember $and takes a LIST, not a dict"],"tags":["milvus","filter","and-operator","dsl"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}