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

filterVector() throws when a filters object contains an AND key whose value is not an array. After key normalization ($and maps to AND), the compound AND filter is expected to be a list of sub-filter dicts applied with .every(); a non-array value cannot be evaluated, so the provider fails fast rather than silently skipping the filter.

Source

Thrown at mem0-ts/src/oss/src/vector_stores/databricks.ts:1583

    }

    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 normalizedKey = keyMap[key] || key;
      if (!(normalizedKey in normalized)) {
        normalized[normalizedKey] = value;
      }
    }

    for (const [key, value] of Object.entries(normalized)) {
      if (key === "AND") {
        if (!Array.isArray(value)) {
          throw new Error(
            `AND filter value must be a list of filter dicts, got ${typeof value}`,
          );
        }
        if (
          !value.every((entry: SearchFilters) =>
            this.filterVector(vector, entry),
          )
        ) {
          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}`,
          );
        }
        if (
          !value.some((entry: SearchFilters) =>

View on GitHub (pinned to 001c235229)

Solutions

  1. Wrap AND branches in an array: { AND: [ {...}, {...} ] }.
  2. When building filters programmatically, always push sub-filters into an array even for a single condition.
  3. If the filter came from JSON config, validate its shape before passing it to search().

Example fix

// before
store.search(query, 5, { $and: { user_id: 'u1', run_id: 'r2' } });

// after
store.search(query, 5, { $and: [{ user_id: 'u1' }, { run_id: 'r2' }] });
Defensive patterns

Strategy: type-guard

Validate before calling

const isCompoundList = (f: Record<string, unknown>) =>
  (['AND', 'OR', 'NOT'] as const).every((k) => !(k in f) || Array.isArray(f[k]));
if (!isCompoundList(filters)) throw new Error('AND/OR/NOT branches must be arrays');

Type guard

type Branch = Record<string, string | number | boolean>;
interface SafeFilters {
  AND?: Branch[];
  OR?: Branch[];
  NOT?: Branch[];
  [k: string]: unknown;
}
const isSafeFilters = (f: unknown): f is SafeFilters =>
  typeof f === 'object' && f !== null &&
  Object.entries(f).every(([k, v]) =>
    ['AND', 'OR', 'NOT'].includes(k) ? Array.isArray(v) : true,
  );

Try / catch

try {
  await store.search(q, k, filters);
} catch (e) {
  if (e instanceof Error && e.message.includes('AND filter value must be a list')) {
    // rewrite { AND: {...} } to { AND: [{...}] } and retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling search()/list() with filters like { AND: { user_id: 'u1' } } (object instead of array), or { $and: 'user_id' } (string). Correct shape is { AND: [{ user_id: 'u1' }, { run_id: 'r1' }] }.

Common situations: Translating Qdrant-style or Python-dict filter syntax into the TS SDK and forgetting the list wrapper around $and branches; building filters dynamically where an empty/single condition collapses to a bare object.

Related errors


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