{"record":{"id":"72f148f64a6cfe85","repo":"mem0ai/mem0","slug":"filter-value-for-key-must-be-a-string-number","errorCode":null,"errorMessage":"Filter value for '${key}' must be a string, number, or boolean, got ${Array.isArray(value) ? \"an array\" : \"an object\"}.","messagePattern":"Filter value for '(.+?)' must be a string, number, or boolean, got (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mem0-ts/src/oss/src/vector_stores/opensearch.ts","lineNumber":640,"sourceCode":"    return clauses.length === 1 ? clauses[0] : { bool: { filter: clauses } };\n  }\n\n  private payloadField(key: string, keyword: boolean): string {\n    if (key.startsWith(\"payload.\")) {\n      return keyword && !key.endsWith(\".keyword\") ? `${key}.keyword` : key;\n    }\n\n    const field = `payload.${key}`;\n    return keyword ? `${field}.keyword` : field;\n  }\n\n  // Filter values become OpenSearch term/terms/range/wildcard leaves. Allowing\n  // an object here lets a caller inject raw query parameters (e.g. a `term`\n  // object form with `boost`/`case_insensitive`), so reject non-scalar leaves.\n  // Mirrors the Python SDK's `_validate_filter` scalar allow-list (PR #5986).\n  private assertScalarValue(key: string, value: any): void {\n    if (value !== null && typeof value === \"object\") {\n      throw new Error(\n        `Filter value for '${key}' must be a string, number, or boolean, got ` +\n          `${Array.isArray(value) ? \"an array\" : \"an object\"}.`,\n      );\n    }\n  }\n\n  private assertScalarArray(key: string, value: any): void {\n    if (!Array.isArray(value)) {\n      throw new Error(`Filter value for '${key}' must be an array.`);\n    }\n    value.forEach((item) => this.assertScalarValue(key, item));\n  }\n}\n","sourceCodeStart":622,"sourceCodeEnd":654,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/vector_stores/opensearch.ts#L622-L654","documentation":"Filter leaf values become OpenSearch term/terms/range/wildcard query parameters. Accepting arbitrary objects would let a caller inject raw query DSL (e.g. a term object with boost or case_insensitive, changing query semantics or exfiltrating internals), so the store enforces a scalar allow-list (string/number/boolean/null) mirroring the Python SDK's _validate_filter. Non-scalar leaves throw with the offending key and actual type.","triggerScenarios":"Passing { user_id: { term: { value: 'a', boost: 2 } } } or any object/array where a scalar is expected, e.g. { user_id: ['a','b'] } where the operator expects a scalar (contains, eq).","commonSituations":"Attempts to hand-tune OpenSearch relevance via query DSL inside filters; accidentally passing an array to a scalar operator; filters built from unvalidated user input where a client sends an object.","solutions":["Pass plain scalars for scalar operators: { user_id: 'a' } or { memory: { contains: 'text' } }.","For membership use the dedicated in operator with an array: { tag: { in: ['a','b'] } } — do not inline DSL objects.","Validate external filter input against a schema (zod/etc.) before handing it to search."],"exampleFix":"// before\nfilters = { user_id: { value: 'alice', boost: 2 } };\n\n// after\nfilters = { user_id: 'alice' };","handlingStrategy":"type-guard","validationCode":"for (const [field, ops] of Object.entries(fieldFilters)) {\n  for (const [op, v] of Object.entries(ops || {})) {\n    if (v !== null && typeof v === 'object' && !['in','nin'].includes(op)) {\n      throw new TypeError(`Field '${field}' operator '${op}' expects a scalar`);\n    }\n  }\n}","typeGuard":"const isScalar = (v: unknown): v is string | number | boolean | null =>\n  v === null || ['string', 'number', 'boolean'].includes(typeof v);","tryCatchPattern":null,"preventionTips":["Pass plain scalars; never inline OpenSearch DSL in filters","Schema-validate user-supplied filters (zod)","Use the in operator for arrays instead of raw terms objects"],"tags":["opensearch","filters","security","validation"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}