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

  1. Whitelist filter keys on your side: only allow known keys such as user_id, agent_id, run_id
  2. Sanitize/normalize keys to [A-Za-z0-9_] before calling search with filters
  3. 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

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


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/0b6214e812a6e94c. Report an issue: GitHub.