{"record":{"id":"a98218ff8291b8a4","repo":"mem0ai/mem0","slug":"filter-value-for-json-stringify-key-must-be-a","errorCode":null,"errorMessage":"Filter value for ${JSON.stringify(key)} must be a string, number, or boolean, got ${typeof value}","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/milvus.ts","lineNumber":253,"sourceCode":"      if (!Milvus.SAFE_FILTER_KEY.test(key)) {\n        throw new Error(`Invalid filter key: ${JSON.stringify(key)}`);\n      }\n      if (value === \"*\") {\n        // Wildcard - match any value. Milvus has no direct wildcard, so skip\n        // the clause rather than emitting a literal `== \"*\"` that matches\n        // nothing. Mirrors the Python provider (#6187) and the chroma/pinecone\n        // stores.\n        continue;\n      }\n      if (typeof value === \"string\") {\n        // Escape backslashes before quotes so a value can't break out of the\n        // string literal (order matters, exactly as in the Python provider).\n        const escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n        operands.push(`(metadata[\"${key}\"] == \"${escaped}\")`);\n      } else if (typeof value === \"number\" || typeof value === \"boolean\") {\n        operands.push(`(metadata[\"${key}\"] == ${value})`);\n      } else {\n        throw new Error(\n          `Filter value for ${JSON.stringify(key)} must be a string, number, or boolean, got ${typeof value}`,\n        );\n      }\n    }\n    return operands.length > 0 ? operands.join(\" and \") : undefined;\n  }\n\n  /**\n   * Text fed to the BM25 sparse index for a payload. Prefers `textLemmatized`,\n   * then `text_lemmatized`, then raw `data`; truncates to the VarChar limit.\n   */\n  private bm25Text(payload?: Record<string, any>): string {\n    if (!payload) return \"\";\n    const raw =\n      payload.textLemmatized || payload.text_lemmatized || payload.data || \"\";\n    return String(raw).slice(0, 65535);\n  }\n","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/vector_stores/milvus.ts#L235-L271","documentation":"Milvus scalar equality expressions can only compare metadata fields against string, number, or boolean literals; the filter builder emits quoted strings or raw numbers/booleans and rejects everything else (objects, arrays, null is skipped earlier, symbols, undefined handled earlier). Passing e.g. { user_id: { $eq: 'u1' } } or an array value throws with the actual typeof so the caller can fix the shape.","triggerScenarios":"Passing Mongo-style operator objects ({ user_id: { $in: [...] } }) to the Milvus store; reusing a filter built for the memory.ts/other stores that accept richer shapes; sending { tag: ['a','b'] } (arrays are unsupported, unlike some other providers).","commonSituations":"Writing provider-agnostic filter code and forgetting Milvus is equality-only AND-combined; filters deserialized from JSON APIs where a value silently became an object.","solutions":["Flatten to plain equality values: { user_id: 'u1', score: 5, active: true }.","For multiple allowed values, issue one search per value and merge results (no $in support here).","For range/operator semantics, use a different provider or filter results client-side after search."],"exampleFix":"// before\nstore.search(q, 5, { user_id: { $eq: 'u1' } }); // typeof value === 'object' -> throws\n\n// after\nstore.search(q, 5, { user_id: 'u1' });","handlingStrategy":"type-guard","validationCode":"for (const [k, v] of Object.entries(filters || {})) {\n  if (v !== undefined && v !== null && v !== '*' && !['string', 'number', 'boolean'].includes(typeof v)) {\n    throw new Error(`Milvus filter '${k}' must be scalar, got ${typeof v}`);\n  }\n}","typeGuard":"type MilvusFilterValue = string | number | boolean;\nconst isMilvusFilterValue = (v: unknown): v is MilvusFilterValue =>\n  typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';","tryCatchPattern":"try { await store.search(q, 5, filters); }\ncatch (e) {\n  if (e instanceof Error && /must be a string, number, or boolean/.test(e.message)) {\n    // flatten operator objects ({ $eq: x } -> x) and retry\n  } else throw e;\n}","preventionTips":["Milvus filters here are equality-only AND-combined; don't pass Mongo-style operators or arrays.","Type filter payloads as Record<string, string | number | boolean> in your call sites.","Validate external/LLM-generated filter JSON against that scalar schema."],"tags":["milvus","filters","type-validation","vector-store","typescript"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}