mem0ai/mem0 · error · ValueError
Invalid filter key: {key!r}
Error message
Invalid filter key: {key!r} What it means
OpenSearch filter keys become term-clause field names, so they are validated against ^[a-zA-Z_][a-zA-Z0-9_.]*$. This error means a key is not a string or contains characters outside that set (leading digit, dash, space, $, unicode). It blocks malformed DSL injection into the query body.
Source
Thrown at mem0/vector_stores/opensearch.py:24
try:
from opensearchpy import OpenSearch, RequestsHttpConnection
except ImportError:
raise ImportError("OpenSearch requires extra dependencies. Install with `pip install opensearch-py`") from None
from pydantic import BaseModel
from mem0.configs.vector_stores.opensearch import OpenSearchConfig
from mem0.vector_stores.base import VectorStoreBase
logger = logging.getLogger(__name__)
_SAFE_FILTER_KEY = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_.]*$")
_IDENTITY_FILTER_KEYS = ("user_id", "agent_id", "run_id")
def _validate_filter(key: str, value) -> None:
if not isinstance(key, str) or not _SAFE_FILTER_KEY.match(key):
raise ValueError(f"Invalid filter key: {key!r}")
if not isinstance(value, (str, int, float, bool)):
raise ValueError(
f"Filter value for {key!r} must be str, int, float, or bool, "
f"got {type(value).__name__}"
)
def _build_filter_clauses(filters):
"""Build term clauses from every filter key, not just the identity keys."""
filter_clauses = []
for key, value in (filters or {}).items():
if value is None:
continue
if value == "*":
# "Any value" wildcard (a documented Platform pattern): match
# documents where the field exists — as opensearch.ts already
# does for every key — instead of a literal, near-always-empty
# term match on the string "*".View on GitHub (pinned to 001c235229)
Solutions
- Rename keys to match [a-zA-Z_][a-zA-Z0-9_.]* — e.g. user_id, app.version is allowed here (dots OK)
- Whitelist filter keys at your API boundary
- Store unmatchable keys inside the payload under a sanitized alias
Example fix
// before
filters = {"user-id": "alice"}
// after
filters = {"user_id": "alice"} Defensive patterns
Strategy: validation
Validate before calling
import re
SAFE_KEY = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_.]*$")
def sanitize_os_keys(filters: dict) -> dict:
return {re.sub(r"[^a-zA-Z0-9_.]", "_", k): v for k, v in (filters or {}).items()
if isinstance(k, str) and k}
filters = sanitize_os_keys(filters) Type guard
def is_os_safe_key(k) -> bool:
return isinstance(k, str) and bool(re.match(r"^[a-zA-Z_][a-zA-Z0-9_.]*$", k)) Try / catch
try:
store.search(q, vec, filters=filters)
except ValueError as e:
if "Invalid filter key" in str(e):
filters = sanitize_os_keys(filters)
store.search(q, vec, filters=filters)
else:
raise Prevention
- Use snake_case (dots allowed) filter keys
- Validate inbound filter keys against a whitelist
- Keep one canonical filter schema even when multiple vector backends are supported
When it happens
Trigger: filters={"user-id": "u"}, {"2app": 1}, {"user id": "u"}, or a numeric key from parsed JSON passed to search/list on the OpenSearch backend.
Common situations: Sharing filter dicts across backends where one accepted dashes; forwarding raw user input as filter keys; templating filter keys from display labels with spaces.
Related errors
- Filter value for '${key}' must be a string, number, or boole
- Filter value for {key!r} must be str, int, float, or bool, g
- Filter list for '${key}' contains an object, which may conta
- Filter value for '${key}' must be a scalar (string, number,
- ${key} filter value must be an array.
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/c1b861841cf02bf8.
Report an issue: GitHub.