mem0ai/mem0 · error · Error
${key} filter requires a non-empty list
Error message
${key} filter requires a non-empty list What it means
When converting mem0 filters to S3 Vectors filter syntax, $and and $or must map to a non-empty array of sub-filter objects. An empty array (or non-array) throws, because S3 Vectors has no representation for a vacuous conjunction/disjunction and the store refuses to guess semantics.
Source
Thrown at mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:498
payload: this.normalizeMetadata(vector.metadata),
};
}
private convertFilters(filters?: SearchFilters): S3Filter | undefined {
if (!filters || Object.keys(filters).length === 0) {
return undefined;
}
const normalized = this.convertFilterNode(filters);
return this.isNoOpFilter(normalized) ? undefined : normalized;
}
private convertFilterNode(node: Record<string, any>): S3Filter {
const clauses: S3Filter[] = [];
for (const [key, value] of Object.entries(node)) {
if (key === "$and" || key === "$or") {
if (!Array.isArray(value) || value.length === 0) {
throw new Error(`${key} filter requires a non-empty list`);
}
const normalizedEntries = value.map((entry) =>
this.convertFilterNode(entry),
);
const entries = normalizedEntries.filter(
(entry) => !this.isNoOpFilter(entry),
);
if (key === "$and") {
if (entries.some((entry) => this.isAlwaysFalseFilter(entry))) {
return { [ALWAYS_FALSE_FILTER_KEY]: true };
}
if (entries.length === 0) {
continue;
}
} else {
if (normalizedEntries.some((entry) => this.isNoOpFilter(entry))) {
continue;
}View on GitHub (pinned to 001c235229)
Solutions
- Omit the $and/$or key entirely when the conditions list is empty
- Guard when building: const filters = conds.length ? { $and: conds } : undefined
- Validate incoming filter JSON rejects empty logical arrays at the API boundary
Example fix
// before
const filters = { $and: conditions }; // conditions may be []
// after
const filters = conditions.length > 0 ? { $and: conditions } : undefined;
const r = filters ? await vs.search(vec, { filters }) : await vs.search(vec, {}); Defensive patterns
Strategy: validation
Validate before calling
function pruneEmptyLogical(filters: any): any {
const out: any = {};
for (const [k, v] of Object.entries(filters ?? {})) {
if ((k === '$and' || k === '$or') && Array.isArray(v) && v.length === 0) continue;
if (k === '$not' && Array.isArray(v) && v.length === 0) continue;
out[k] = v;
}
return Object.keys(out).length ? out : undefined;
}
const safeFilters = pruneEmptyLogical(filters); Type guard
const isNonEmptyFilterList = (v: unknown, key: string): v is Record<string, any>[] => (key === '$and' || key === '$or' || key === '$not') && Array.isArray(v) && v.length > 0;
Try / catch
try { await vs.search(vec, { filters }); } catch (e) { if (e instanceof Error && e.message.includes('requires a non-empty list')) { /* drop empty $and/$or, retry */ } throw e; } Prevention
- Build logical filters conditionally so empty wrappers are never emitted
- Validate external filter JSON with zod: z.array(z.record(...)).min(1) for $and/$or
- Unit-test filter builders with zero-condition inputs
When it happens
Trigger: filters: { $and: [] }, { $or: [] }, or dynamically built filters where the conditions array ended up empty ({ $and: conds } when conds is []).
Common situations: Programmatic filter building that spreads possibly-empty lists; user-supplied advanced_filters JSON with empty arrays; refactors that strip conditions but keep the wrapper.
Related errors
- $not filter requires a non-empty list
- ${key} filter value must be a list of filter dicts, got ${ty
- ${key} filter list item at index ${i} must be a dict, got ${
- Invalid filter key '${key}': only letters, digits, and under
- Unsupported filter operator: ${op}
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/38b8d1bbfcda2285.
Report an issue: GitHub.