mem0ai/mem0 · error · Error
AND filter value must be a list of filter dicts, got ${typeo
Error message
AND filter value must be a list of filter dicts, got ${typeof value} What it means
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.
Source
Thrown at mem0-ts/src/oss/src/vector_stores/memory.ts:181
// Normalize $or/$not/$and → OR/NOT/AND
const keyMap: Record<string, string> = {
$and: "AND",
$or: "OR",
$not: "NOT",
};
const normalized: Record<string, any> = {};
for (const [key, value] of Object.entries(filters)) {
const normKey = keyMap[key] || key;
if (!(normKey in normalized)) {
normalized[normKey] = value;
}
}
for (const [key, value] of Object.entries(normalized)) {
// Handle logical operators
if (key === "AND") {
if (!Array.isArray(value)) {
throw new Error(
`AND filter value must be a list of filter dicts, got ${typeof value}`,
);
}
// All conditions must match
const allMatch = value.every((sub: SearchFilters) =>
this.filterVector(vector, sub),
);
if (!allMatch) return false;
} else if (key === "OR") {
if (!Array.isArray(value)) {
throw new Error(
`OR filter value must be a list of filter dicts, got ${typeof value}`,
);
}
// At least one condition must match
const anyMatch = value.some((sub: SearchFilters) =>
this.filterVector(vector, sub),
);View on GitHub (pinned to 001c235229)
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.
Example fix
// before
store.search(q, 5, { AND: { user_id: 'u1' } }); // throws
// after
store.search(q, 5, { AND: [{ user_id: 'u1' }] }); Defensive patterns
Strategy: validation
Validate before calling
const LOGICAL = new Set(['AND', 'OR', 'NOT']);
function normalizeLogical(filters: Record<string, any>) {
for (const k of Object.keys(filters)) {
if (LOGICAL.has(k) && !Array.isArray(filters[k])) filters[k] = [filters[k]];
}
return filters;
} Type guard
type Leaf = Record<string, string | number | boolean | string[]>;
interface SearchFilters { AND?: SearchFilters[]; OR?: SearchFilters[]; NOT?: SearchFilters[] } & Leaf
const isFilterArray = (v: unknown): v is SearchFilters[] => Array.isArray(v); Try / catch
try {
await store.search(q, 5, filters);
} catch (e) {
if (e instanceof Error && /AND filter value must be a list/.test(e.message)) {
return store.search(q, 5, normalizeLogical(filters)); // retry with wrapped value
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- OR filter value must be a list of filter dicts, got ${typeof
- NOT filter value must be a list of filter dicts, got ${typeo
- AND filter value must be a list of filter dicts, got ${typeo
- OR filter value must be a list of filter dicts, got ${typeof
- NOT filter value must be a list of filter dicts, got ${typeo
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/0fc1e7a47c31b004.
Report an issue: GitHub.