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

  1. Omit the $and/$or key entirely when the conditions list is empty
  2. Guard when building: const filters = conds.length ? { $and: conds } : undefined
  3. 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

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


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/38b8d1bbfcda2285. Report an issue: GitHub.