mem0ai/mem0 · error · Error
NOT filter value must be a list of filter dicts, got ${typeo
Error message
NOT filter value must be a list of filter dicts, got ${typeof value} What it means
The NOT logical operator in the SQLite MemoryVectorStore expects an array of filter dicts; each sub-condition is evaluated and the vector passes only if none match (every(sub => !filterVector(...))). A non-array value cannot be iterated, so the store throws rather than guessing semantics.
Source
Thrown at mem0-ts/src/oss/src/vector_stores/memory.ts:203
// 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),
);
if (!anyMatch) return false;
} else if (key === "NOT") {
if (!Array.isArray(value)) {
throw new Error(
`NOT filter value must be a list of filter dicts, got ${typeof value}`,
);
}
// None of the conditions should match
const noneMatch = value.every(
(sub: SearchFilters) => !this.filterVector(vector, sub),
);
if (!noneMatch) return false;
} else {
// Regular field condition
if (!this.matchFieldCondition(vector.payload, key, value)) {
return false;
}
}
}
return true;
}View on GitHub (pinned to 001c235229)
Solutions
- Wrap in an array: { NOT: [{ user_id: 'u1' }] }.
- Combine exclusions: { AND: [{ run_id: 'r1' }, ...], NOT: [{ user_id: 'blocked' }] }.
- Write a small normalizeFilters() helper that wraps bare objects under AND/OR/NOT keys.
Example fix
// before
store.search(q, 5, { NOT: { user_id: 'u1' } }); // throws
// after
store.search(q, 5, { NOT: [{ user_id: 'u1' }] }); Defensive patterns
Strategy: validation
Validate before calling
if (filters.NOT !== undefined && !Array.isArray(filters.NOT)) {
filters = { ...filters, NOT: [filters.NOT] };
} Type guard
const isFilterList = (v: unknown): v is SearchFilters[] => Array.isArray(v) && v.every((e) => e && typeof e === 'object' && !Array.isArray(e));
Try / catch
try { await store.search(q, 5, filters); }
catch (e) {
if (e instanceof Error && e.message.startsWith('NOT filter value')) {
// fix shape and retry once
return store.search(q, 5, { ...filters, NOT: [filters.NOT] });
}
throw e;
} Prevention
- Treat AND/OR/NOT uniformly: value is always SearchFilters[].
- Write exclusion filters as { NOT: [{ field: value }] } from the start.
- Guard dynamically-built filter trees with a shape validator before search().
When it happens
Trigger: search(query, topK, { NOT: { user_id: 'u1' } }) instead of { NOT: [{ user_id: 'u1' }] }; copy-pasting an AND/OR shape into NOT without adjusting the value type.
Common situations: Exclusion filters written inline; refactors that change one logical operator but keep the operand shape; mixed AND/NOT nested objects where the nested NOT value is a dict.
Related errors
- AND filter value must be a list of filter dicts, got ${typeo
- OR filter value must be a list of filter dicts, got ${typeof
- 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/e76f35b8cb0fd0bf.
Report an issue: GitHub.