mem0ai/mem0 · error · ValueError

Invalid query: must be a non-empty string.

Error message

Invalid query: must be a non-empty string.

What it means

Raised by _validate_and_trim_search_query when the 'query' argument to Memory.search() is not a Python str at all (None, int, dict, list, bytes). The message text says 'non-empty string' but this specific branch fires for wrong types; the query must be a string because it is embedded and sent to the vector store as text. This is a fail-fast ValueError before any embedding cost.

Source

Thrown at mem0/memory/main.py:247

            )
    if top_k is not None:
        if not isinstance(top_k, int) or isinstance(top_k, bool):
            raise ValueError("top_k must be a valid integer")
        if top_k < 0:
            raise ValueError(
                f"Invalid top_k: {top_k}. Must be a non-negative integer."
            )


def _validate_and_trim_search_query(query: str) -> str:
    """
    Validates and normalizes a search query before embedding/vector search.

    Raises:
        ValueError: If query is not a string or is empty/whitespace-only.
    """
    if not isinstance(query, str):
        raise ValueError("Invalid query: must be a non-empty string.")
    trimmed = query.strip()
    if not trimmed:
        raise ValueError("Invalid query: cannot be empty or whitespace-only.")
    return trimmed


def _is_sensitive_field(field_name: str) -> bool:
    """Check if a field should be redacted for telemetry safety.

    Uses a layered approach:
    1. Runtime fields (allowlist) — always preserved, highest priority.
    2. Exact deny list — known secret field names.
    3. Suffix deny list — catches patterns like db_password, auth_secret, etc.
    """
    name = field_name.lower().strip()
    if name in _RUNTIME_FIELDS:
        return False
    if name in _SENSITIVE_FIELDS_EXACT:

View on GitHub (pinned to 001c235229)

Solutions

  1. Guard with 'if not query: return []' (or raise your own domain error) before calling search().
  2. Extract the actual string: m.search(payload['question'], ...) instead of m.search(payload, ...).
  3. Coerce explicitly with str(query) only when you know the value is stringable text.
  4. Default the parameter to a sensible sentinel and skip the search call when unset.

Example fix

# before
result = m.search(user_question, filters=f)  # user_question may be None

# after
if not isinstance(user_question, str) or not user_question.strip():
    return []
result = m.search(user_question, filters=f)
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(query, str):
    raise TypeError(f"query must be str, got {type(query).__name__}")

Type guard

def is_searchable_query(q) -> bool:
    return isinstance(q, str) and len(q.strip()) > 0

Prevention

When it happens

Trigger: m.search(None, filters={'user_id':'u1'}); m.search(12345, ...); m.search({'question': '...'}, ...) passing a dict instead of its content; a variable that is None because an upstream LLM/tool call returned nothing; bytes from a network payload not decoded.

Common situations: Optional query parameters in a wrapper function defaulting to None and being forwarded anyway; template rendering returning None on failure; dataclass/JSON fields assumed to be strings but sometimes null.

Related errors


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