mem0ai/mem0 · error · ValueError
Invalid filter key: {key!r}
Error message
Invalid filter key: {key!r} What it means
Milvus._create_filter() rejects filter keys that do not match _SAFE_FILTER_KEY (an identifier-safe regex) before interpolating them into a Milvus filter expression string. This is SQL-injection-style protection for the expression language: arbitrary key bytes could break out of the metadata["key"] quoting.
Source
Thrown at mem0/vector_stores/milvus.py:161
data = [_build_record(idx, embedding, metadata) for idx, embedding, metadata in zip(ids, vectors, payloads)]
self.client.insert(collection_name=self.collection_name, data=data, **kwargs)
_SAFE_FILTER_KEY = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
def _create_filter(self, filters: dict):
"""Prepare filters for efficient query.
Args:
filters (dict): filters [user_id, agent_id, run_id]
Returns:
str: formated filter.
"""
operands = []
for key, value in filters.items():
if not self._SAFE_FILTER_KEY.match(key):
raise ValueError(f"Invalid filter key: {key!r}")
if value == "*":
# Wildcard - match any value (MilvusDB doesn't have direct wildcard, so we skip this filter)
continue
elif isinstance(value, str):
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
operands.append(f'(metadata["{key}"] == "{escaped}")')
elif isinstance(value, (int, float, bool)):
operands.append(f'(metadata["{key}"] == {value})')
else:
raise ValueError(
f"Filter value for {key!r} must be str, int, float, or bool, "
f"got {type(value).__name__}"
)
return " and ".join(operands)
def _parse_output(self, data: list):
"""View on GitHub (pinned to 001c235229)
Solutions
- Whitelist filter keys on your side: only allow known keys such as user_id, agent_id, run_id
- Sanitize/normalize keys to [A-Za-z0-9_] before calling search with filters
- Never pass untrusted input unmodified in the filters dict
Example fix
# before
filters = {raw_user_key: raw_user_value} # raw_user_key = 'user_id" or "1'
results = m.search('query', filters=filters)
# after
import re
SAFE = re.compile(r'^[A-Za-z0-9_]+$')
filters = {k: v for k, v in raw_filters.items() if SAFE.match(k)}
results = m.search('query', filters=filters) Defensive patterns
Strategy: validation
Validate before calling
import re
SAFE_KEY = re.compile(r'^[A-Za-z0-9_]+$')
ALLOWED = {'user_id', 'agent_id', 'run_id'}
def safe_filters(filters):
return {k: v for k, v in (filters or {}).items() if k in ALLOWED and SAFE_KEY.match(k)} Type guard
def is_safe_filter_key(key) -> bool:
import re
return isinstance(key, str) and re.match(r'^[A-Za-z0-9_]+$', key) is not None Try / catch
try:
m.search('query', filters=filters)
except ValueError as e:
if 'Invalid filter key' in str(e):
# strip offending keys and retry with sanitized filters
raise Prevention
- Whitelist filter keys at the API boundary — never forward untrusted keys
- Keep filter keys to identifier characters ([A-Za-z0-9_])
- Log rejected filter keys to detect injection attempts
When it happens
Trigger: Passing filters with keys containing quotes, brackets, spaces, or expression operators — e.g. filters={'user_id" == "x': 1} or keys built from untrusted user input like {'run_id; drop': 'abc'}.
Common situations: Passing raw user-supplied JSON as the filters dict; filter keys containing hyphens or dots; constructing nested filter dicts (lists/dicts as keys after str()).
Related errors
- Invalid filter key: ${JSON.stringify(key)}
- Filter list for '${key}' contains an object, which may conta
- Filter value for '${key}' must be a scalar (string, number,
- Filter value for '${key}' must be a string, number, or boole
- Filter value for {key!r} must be a scalar (str, int, float,
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/0b6214e812a6e94c.
Report an issue: GitHub.