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

  1. Wrap the sub-filter in an array: { AND: [{ user_id: 'u1' }, { run_id: 'r1' }] }.
  2. If you only have one condition, drop the AND wrapper entirely: { user_id: 'u1' }.
  3. 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

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


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