{"record":{"id":"fd22db32b2201fc2","repo":"mem0ai/mem0","slug":"filter-value-for-key-r-must-be-str-int-float-fd22db","errorCode":null,"errorMessage":"Filter value for {key!r} must be str, int, float, or bool, got {type(value).__name__}","messagePattern":"Filter value for (.+?) must be str, int, float, or bool, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mem0/vector_stores/opensearch.py","lineNumber":26,"sourceCode":"except ImportError:\n    raise ImportError(\"OpenSearch requires extra dependencies. Install with `pip install opensearch-py`\") from None\n\nfrom pydantic import BaseModel\n\nfrom mem0.configs.vector_stores.opensearch import OpenSearchConfig\nfrom mem0.vector_stores.base import VectorStoreBase\n\nlogger = logging.getLogger(__name__)\n\n_SAFE_FILTER_KEY = re.compile(r\"^[a-zA-Z_][a-zA-Z0-9_.]*$\")\n_IDENTITY_FILTER_KEYS = (\"user_id\", \"agent_id\", \"run_id\")\n\n\ndef _validate_filter(key: str, value) -> None:\n    if not isinstance(key, str) or not _SAFE_FILTER_KEY.match(key):\n        raise ValueError(f\"Invalid filter key: {key!r}\")\n    if not isinstance(value, (str, int, float, bool)):\n        raise ValueError(\n            f\"Filter value for {key!r} must be str, int, float, or bool, \"\n            f\"got {type(value).__name__}\"\n        )\n\n\ndef _build_filter_clauses(filters):\n    \"\"\"Build term clauses from every filter key, not just the identity keys.\"\"\"\n    filter_clauses = []\n    for key, value in (filters or {}).items():\n        if value is None:\n            continue\n        if value == \"*\":\n            # \"Any value\" wildcard (a documented Platform pattern): match\n            # documents where the field exists — as opensearch.ts already\n            # does for every key — instead of a literal, near-always-empty\n            # term match on the string \"*\".\n            _validate_filter(key, value)\n            filter_clauses.append({\"exists\": {\"field\": f\"payload.{key}\"}})","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0/vector_stores/opensearch.py#L8-L44","documentation":"OpenSearch filter values become term query values, which must be scalars (str, int, float, bool). This error fires when a filter value is a list, dict, None-with-type or other object — the backend has no operator syntax (no $in/$gt), so non-scalars cannot be expressed and might inject query DSL.","triggerScenarios":"filters={\"user_id\": [\"a\",\"b\"]}, {\"range\": {\"gte\": 1}}, or a nested object passed as a filter value to the OpenSearch backend's search.","commonSituations":"Copying Qdrant/Mongo-style structured filters to OpenSearch; hoping for range/in semantics that this term-only builder does not support; forwarding untyped request bodies.","solutions":["Use scalar equality only; for multi-value, issue one search per value and merge","For range/wildcard needs, query OpenSearch directly with your own DSL instead of the filters parameter","Validate filters shape before calling (see guard below)"],"exampleFix":"// before\nfilters = {\"user_id\": [\"a\", \"b\"], \"ts\": {\"$gte\": 100}}\n\n// after\nresults = [r for v in [\"a\", \"b\"] for r in store.search(q, vec, k, {\"user_id\": v})]","handlingStrategy":"type-guard","validationCode":"def assert_scalar(filters: dict) -> None:\n    bad = [k for k, v in (filters or {}).items() if not isinstance(v, (str, int, float, bool))]\n    if bad:\n        raise TypeError(f\"non-scalar filter values for {bad}\")\n\nassert_scalar(filters)\nstore.search(query, vector, top_k, filters=filters)","typeGuard":"def is_scalar_filters(filters: dict) -> bool:\n    return all(isinstance(v, (str, int, float, bool)) for v in (filters or {}).values())","tryCatchPattern":"try:\n    store.search(q, vec, filters=filters)\nexcept ValueError as e:\n    if \"must be str, int, float, or bool\" in str(e):\n        filters = {k: v for k, v in filters.items() if isinstance(v, (str, int, float, bool))}\n        store.search(q, vec, filters=filters)\n    else:\n        raise","preventionTips":["Term-equality only on this backend; fan out lists client-side","Type your public filter contract as scalars","Use direct OpenSearch DSL for range/wildcard needs"],"tags":["opensearch","filters","validation","security"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}