mem0ai/mem0 · error · Error

${key} filter list item at index ${i} must be a dict, got ${

Error message

${key} filter list item at index ${i} must be a dict, got ${typeof item}

What it means

Each element of an AND/OR/NOT array in a Qdrant filter must itself be a filter object (dict). If a list item is a primitive, null, or an array, this error reports the offending index. It prevents building an invalid Qdrant filter that would fail opaquely server-side.

Source

Thrown at mem0-ts/src/oss/src/vector_stores/qdrant.ts:246

    const should: (QdrantCondition | QdrantFilter)[] = [];
    const mustNot: (QdrantCondition | QdrantFilter)[] = [];

    for (const [key, value] of Object.entries(normalized)) {
      // Handle logical operators
      if (key === "AND" || key === "OR" || key === "NOT") {
        if (!Array.isArray(value)) {
          throw new Error(
            `${key} filter value must be a list of filter dicts, got ${typeof value}`,
          );
        }
        for (let i = 0; i < value.length; i++) {
          const item = value[i];
          if (
            typeof item !== "object" ||
            item === null ||
            Array.isArray(item)
          ) {
            throw new Error(
              `${key} filter list item at index ${i} must be a dict, got ${typeof item}`,
            );
          }
        }

        if (key === "AND") {
          for (const sub of value) {
            const built = this.createFilter(sub);
            if (built) {
              must.push(built);
            }
          }
        } else if (key === "OR") {
          for (const sub of value) {
            const built = this.createFilter(sub);
            if (built) {
              should.push(built);
            }

View on GitHub (pinned to 001c235229)

Solutions

  1. Ensure every element of AND/OR/NOT arrays is a filter object, wrapping bare values as { field: { eq: value } }
  2. Filter out null/undefined entries before passing: conds.filter(Boolean)
  3. Validate user-supplied filter JSON at the API boundary before forwarding it to search()

Example fix

// before
const r = await vs.search(vec, { filters: { OR: [ { tag: 'a' }, 'plain' ] } });

// after
const r = await vs.search(vec, { filters: { OR: [ { tag: 'a' }, { tag: 'plain' } ] } });
Defensive patterns

Strategy: validation

Validate before calling

function cleanLogicalList(items: unknown): Record<string, any>[] {
  return (items as unknown[])
    .filter(i => i !== null && i !== undefined)
    .map(i => (i && typeof i === 'object' && !Array.isArray(i)) ? i : null)
    .filter((i): i is Record<string, any> => i !== null);
}
filters.AND = cleanLogicalList(filters.AND);

Type guard

const isFilterDict = (v: unknown): v is Record<string, any> =>
  !!v && typeof v === 'object' && !Array.isArray(v);

Try / catch

try { await vs.search(vec, { filters }); } catch (e) { if (e instanceof Error && e.message.includes('must be a dict')) { /* drop or wrap offending items, retry */ } throw e; }

Prevention

When it happens

Trigger: filters: { AND: [ { a: 1 }, 'b' ] }, { OR: [ null ] }, or { NOT: [ ['a','b'] ] } — any logical list containing a non-object item.

Common situations: Spreading mixed content into a conditions array ({ AND: [...conds, someFlag] }); JSON filters from clients where a value is null; flattening nested arrays incorrectly.

Related errors


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