microsoft/semantic-kernel · error · VectorStoreOperationException
Error evaluating filter: {e}
Error message
Error evaluating filter: {e} What it means
Thrown by _get_filtered_records (in_memory.py:734-735) as VectorStoreOperationException wrapping any exception raised while preparing string filters inside _parse_and_validate_filter. The original error is chained as __cause__, and its text is embedded after 'Error evaluating filter: '. So this is the top-level message users see; the precise reason (oversized, invalid python, disallowed node, blocked attribute, etc.) lives in e.__cause__.
Source
Thrown at python/semantic_kernel/connectors/in_memory.py:735
for idx, key in enumerate(return_records.keys()):
if idx >= skip:
returned += 1
rec = self.inner_storage[key]
rec[IN_MEMORY_SCORE_KEY] = return_records[key]
yield rec
if returned >= top:
break
def _get_filtered_records(self, options: VectorSearchOptions) -> dict[TKey, AttributeDict]:
if not options.filter:
return self.inner_storage
try:
callable_filters = [
self._parse_and_validate_filter(filter) if isinstance(filter, str) else filter
for filter in ([options.filter] if not isinstance(options.filter, list) else options.filter)
]
except Exception as e:
raise VectorStoreOperationException(f"Error evaluating filter: {e}") from e
filtered_records: dict[TKey, AttributeDict] = {}
for key, record in self.inner_storage.items():
for filter in callable_filters:
if self._run_filter(filter, record):
filtered_records[key] = record
return filtered_records
def _parse_and_validate_filter(self, filter_str: str) -> Callable:
"""Parse and validate a string filter as a lambda expression, then return the callable.
Uses an allowlist approach - only explicitly permitted AST node types and function names
are allowed. This can be customized by overriding `allowed_filter_ast_nodes` and
`allowed_filter_functions` class attributes.
"""
if len(filter_str) > self.max_filter_source_length:
raise VectorStoreOperationException("Filter string exceeds the maximum allowed length.")
try:View on GitHub (pinned to c028a0c7dc)
Solutions
- Inspect e.__cause__ (or the text after the colon) to find the specific failure, then apply the matching fix (1334-1339).
- If the filter is dynamically built, validate it with ast.parse + a lambda check before passing it to search.
- Pass options.filter as a Python callable to bypass string parsing entirely when the filter is trusted and complex.
- Catch VectorStoreOperationException at the search boundary and fall back to an unfiltered or simpler query.
Example fix
# before opts = VectorSearchOptions(filter="x.id == 1") # not a lambda -> wrapped error # after opts = VectorSearchOptions(filter="lambda x: x.id == 1")
Defensive patterns
Strategy: try-catch
Validate before calling
import ast
SAFE_NODES = {
ast.Expression, ast.Lambda, ast.arguments, ast.arg, ast.Compare, ast.BoolOp,
ast.UnaryOp, ast.And, ast.Or, ast.Not, ast.Eq, ast.NotEq, ast.Lt, ast.LtE,
ast.Gt, ast.GtE, ast.In, ast.NotIn, ast.Is, ast.IsNot, ast.Name, ast.Load,
ast.Attribute, ast.Subscript, ast.Slice, ast.Constant, ast.List, ast.Tuple,
ast.Set, ast.Dict, ast.BinOp, ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod,
ast.FloorDiv, ast.Call,
}
def preflight_filter(expr: str, *, max_len: int = 2048, max_nodes: int = 128) -> None:
if len(expr) > max_len:
raise ValueError("filter too long")
try:
tree = ast.parse(expr, mode="eval")
except SyntaxError as e:
raise ValueError(f"invalid python: {e}") from e
if not (isinstance(tree, ast.Expression) and isinstance(tree.body, ast.Lambda)):
raise ValueError("filter must be a lambda expression")
blocked = {"__class__", "__globals__", "__subclasses__", "__builtins__", "__code__"}
for n in ast.walk(tree):
if isinstance(n, ast.Attribute) and n.attr in blocked:
raise ValueError(f"blocked attribute: {n.attr}")
if type(n) not in SAFE_NODES:
raise ValueError(f"disallowed node: {type(n).__name__}")
if sum(1 for _ in ast.walk(tree)) > max_nodes:
raise ValueError("filter too complex")
Try / catch
try:
results = await collection.search(search_type=SearchType.VECTOR, options=opts)
except VectorStoreOperationException as e:
logger.warning("filter rejected: %s", e.__cause__ or e)
results = None Prevention
- Always read e.__cause__ to find the real filter failure.
- Preflight dynamically built filter strings with ast.parse.
- Prefer callable filters for trusted, complex logic.
When it happens
Trigger: Any string filter that fails parse-time validation, e.g. a typo'd lambda, an oversized string, a disallowed AST node, or a blocked dunder attribute; also fires for a non-string/non-callable filter value that breaks the comprehension.
Common situations: Building filter strings dynamically and hitting a limit or a typo; user-supplied filter text that is malformed; passing a dict instead of a string/callable as options.filter.
Related errors
- Attribute '{node.func.attr}' is not callable in filter expre
- Call target node type '{type(node.func).__name__}' is not al
- Comparison operator '{type(operator_node).__name__}' is not
- Addition in filter expressions is only allowed for numeric v
- Multiplication in filter expressions is only allowed for num
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/9e7d8ef1903ecfc0.
Report an issue: GitHub.