{"record":{"id":"0fc1e7a47c31b004","repo":"mem0ai/mem0","slug":"and-filter-value-must-be-a-list-of-filter-dicts-g-0fc1e7","errorCode":null,"errorMessage":"AND filter value must be a list of filter dicts, got ${typeof value}","messagePattern":"AND filter value must be a list of filter dicts, got (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mem0-ts/src/oss/src/vector_stores/memory.ts","lineNumber":181,"sourceCode":"    // Normalize $or/$not/$and → OR/NOT/AND\n    const keyMap: Record<string, string> = {\n      $and: \"AND\",\n      $or: \"OR\",\n      $not: \"NOT\",\n    };\n    const normalized: Record<string, any> = {};\n    for (const [key, value] of Object.entries(filters)) {\n      const normKey = keyMap[key] || key;\n      if (!(normKey in normalized)) {\n        normalized[normKey] = value;\n      }\n    }\n\n    for (const [key, value] of Object.entries(normalized)) {\n      // Handle logical operators\n      if (key === \"AND\") {\n        if (!Array.isArray(value)) {\n          throw new Error(\n            `AND filter value must be a list of filter dicts, got ${typeof value}`,\n          );\n        }\n        // All conditions must match\n        const allMatch = value.every((sub: SearchFilters) =>\n          this.filterVector(vector, sub),\n        );\n        if (!allMatch) return false;\n      } else if (key === \"OR\") {\n        if (!Array.isArray(value)) {\n          throw new Error(\n            `OR filter value must be a list of filter dicts, got ${typeof value}`,\n          );\n        }\n        // At least one condition must match\n        const anyMatch = value.some((sub: SearchFilters) =>\n          this.filterVector(vector, sub),\n        );","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/vector_stores/memory.ts#L163-L199","documentation":"The SQLite-backed MemoryVectorStore evaluates search filters in JS. When the normalized filter object contains an 'AND' key, it requires the value to be an array of sub-filter objects (each recursively evaluated via filterVector). Passing a single filter object, a scalar, or undefined instead of a list throws immediately.","triggerScenarios":"Calling search(query, topK, { AND: { user_id: 'u1' } }) instead of { AND: [{ user_id: 'u1' }] }; building filters dynamically and assigning AND from a non-array variable; porting a filter shape from another store that accepts a bare object.","commonSituations":"Misreading the SearchFilters type (AND?: SearchFilters[] vs SearchFilters); combining camelCase keys (and/AND) that fall through keyMap normalization into the logical-operator branch with the wrong value shape.","solutions":["Wrap the sub-filter in an array: { AND: [{ user_id: 'u1' }, { run_id: 'r1' }] }.","If you only have one condition, drop the AND wrapper entirely: { user_id: 'u1' }.","Add a type assertion or runtime check before calling search to enforce SearchFilters[] for logical keys."],"exampleFix":"// before\nstore.search(q, 5, { AND: { user_id: 'u1' } }); // throws\n\n// after\nstore.search(q, 5, { AND: [{ user_id: 'u1' }] });","handlingStrategy":"validation","validationCode":"const LOGICAL = new Set(['AND', 'OR', 'NOT']);\nfunction normalizeLogical(filters: Record<string, any>) {\n  for (const k of Object.keys(filters)) {\n    if (LOGICAL.has(k) && !Array.isArray(filters[k])) filters[k] = [filters[k]];\n  }\n  return filters;\n}","typeGuard":"type Leaf = Record<string, string | number | boolean | string[]>;\ninterface SearchFilters { AND?: SearchFilters[]; OR?: SearchFilters[]; NOT?: SearchFilters[] } & Leaf\nconst isFilterArray = (v: unknown): v is SearchFilters[] => Array.isArray(v);","tryCatchPattern":"try {\n  await store.search(q, 5, filters);\n} catch (e) {\n  if (e instanceof Error && /AND filter value must be a list/.test(e.message)) {\n    return store.search(q, 5, normalizeLogical(filters)); // retry with wrapped value\n  }\n  throw e;\n}","preventionTips":["Type your filters as SearchFilters and let the compiler enforce AND/OR/NOT: SearchFilters[].","Never assign a bare object to AND/OR/NOT; single conditions don't need a logical wrapper.","Normalize at the API boundary if filters arrive as arbitrary JSON."],"tags":["filters","sqlite","vector-store","validation","typescript"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}