mem0ai/mem0 · error · Error

${key} filter value must be an array.

Error message

${key} filter value must be an array.

What it means

buildMetadataVertexFilter() translates $and/$or keys into Neptune's andAll/orAll filter combinators, which take arrays of nested filters. If the value under $and or $or is not an array (a single filter object, a scalar), the recursive .map over sub-entries is impossible, so the store throws before building the vertex filter.

Source

Thrown at mem0-ts/src/oss/src/vector_stores/neptune_analytics.ts:493

      conditions.push(metadataFilter);
    }

    return this.combineVertexFilters("andAll", conditions)!;
  }

  private buildMetadataVertexFilter(
    filters?: SearchFilters,
  ): NeptuneVertexFilter | undefined {
    const operations: NeptuneVertexFilter[] = [];

    for (const [key, value] of Object.entries(filters || {})) {
      if (value === undefined) {
        continue;
      }

      if (key === "$and" || key === "$or") {
        if (!Array.isArray(value)) {
          throw new Error(`${key} filter value must be an array.`);
        }

        const nested = value
          .map((entry) => this.buildMetadataVertexFilter(entry))
          .filter((entry): entry is NeptuneVertexFilter => !!entry);
        const joiner = key === "$and" ? "andAll" : "orAll";
        const combined = this.combineVertexFilters(joiner, nested);
        if (combined) {
          operations.push(combined);
        }
        continue;
      }

      if (key === "$not") {
        if (!Array.isArray(value)) {
          throw new Error("$not filter value must be an array.");
        }

View on GitHub (pinned to 001c235229)

Solutions

  1. Use arrays: { $and: [{ user_id: 'u1' }, { run_id: 'r1' }] }.
  2. For plain conjunction just put fields side by side: { user_id: 'u1', run_id: 'r1' } (implicitly ANDed).
  3. Validate the $-tree shape (arrays under $and/$or/$not) before calling search.

Example fix

// before
store.search(q, 5, { $and: { user_id: 'u1', run_id: 'r1' } }); // throws

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

Strategy: validation

Validate before calling

for (const k of ['$and', '$or']) {
  if (filters[k] !== undefined && !Array.isArray(filters[k])) filters[k] = [filters[k]];
}

Type guard

type NeptuneFilters = Record<string, unknown> & { $and?: NeptuneFilters[]; $or?: NeptuneFilters[]; $not?: NeptuneFilters[] };
const isNeptuneFilterArray = (v: unknown): v is NeptuneFilters[] => Array.isArray(v);

Try / catch

try { await store.search(q, 5, filters); }
catch (e) {
  if (e instanceof Error && /\$and|\$or filter value must be an array/.test(e.message)) {
    return store.search(q, 5, wrapLogicalKeys(filters)); // wrap and retry once
  }
  throw e;
}

Prevention

When it happens

Trigger: search(q, k, { $and: { user_id: 'u1', run_id: 'r1' } }) instead of { $and: [{ user_id: 'u1' }, { run_id: 'r1' }] }; filters received from JSON where the array was collapsed; mixing $-prefixed keys (Neptune style) with the AND style of other providers.

Common situations: Assuming $and takes a dict (Mongo-influenced mental model); reusing filter shapes across providers; LLM-generated filter JSON with inconsistent nesting.

Related errors


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