mem0ai/mem0 · error · Error

Filter value for '${key}' must be a scalar (string, number,

Error message

Filter value for '${key}' must be a scalar (string, number, boolean), not an object. Objects may contain MongoDB query operators.

What it means

A non-array object filter value is rejected by the MongoDB provider because MongoDB would parse it as a query-operator expression ({ user_id: { $gt: 'a' } }), enabling NoSQL injection and unpredictable queries. Only scalars (string, number, boolean) are allowed as direct values. The error names the offending key so the caller can locate the bad field.

Source

Thrown at mem0-ts/src/oss/src/vector_stores/mongodb.ts:200

    }
  }

  private validateFilterValue(key: string, value: any): void {
    if (typeof value === "object" && value !== null) {
      if (Array.isArray(value)) {
        for (const item of value) {
          if (
            typeof item === "object" &&
            item !== null &&
            !Array.isArray(item)
          ) {
            throw new Error(
              `Filter list for '${key}' contains an object, which may contain MongoDB query operators.`,
            );
          }
        }
      } else {
        throw new Error(
          `Filter value for '${key}' must be a scalar (string, number, boolean), not an object. Objects may contain MongoDB query operators.`,
        );
      }
    }
  }

  async insert(
    vectors: number[][],
    ids: string[],
    payloads: Record<string, any>[],
  ): Promise<void> {
    await this.initialize();

    const documents = vectors.map((vector, idx) => ({
      _id: ids[idx] as any,
      embedding: vector,
      payload: payloads[idx] || {},
    }));

View on GitHub (pinned to 001c235229)

Solutions

  1. Use flat equality: { user_id: 'u1', active: true }.
  2. Serialize Dates to string/number before filtering: { created_at: date.toISOString() }.
  3. Enforce a zod/schema check on external filter input allowing only string|number|boolean|scalar[].

Example fix

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

// after
store.search(q, 5, { user_id: 'u1' });
Defensive patterns

Strategy: type-guard

Validate before calling

for (const [k, v] of Object.entries(filters || {})) {
  if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
    throw new Error(`Filter '${k}' must be scalar, got object`);
  }
}

Type guard

const isMongoSafeFilters = (f: unknown): f is Record<string, string | number | boolean | (string | number | boolean)[]> =>
  !!f && typeof f === 'object' && Object.values(f).every(
    (v) => ['string', 'number', 'boolean'].includes(typeof v) || (Array.isArray(v) && v.every((i) => ['string', 'number', 'boolean'].includes(typeof i)))
  );

Try / catch

try { await store.search(q, 5, filters); }
catch (e) {
  if (e instanceof Error && e.message.includes('must be a scalar')) {
    // flatten { $eq: x } to x, serialize Dates to ISO strings, retry
  } else throw e;
}

Prevention

When it happens

Trigger: search(query, topK, { user_id: { $eq: 'u1' } }); { ts: { $gte: 123 } }; null/Date/Buffer values typed as object; filters built by spreading nested objects ({ ...{ meta: { deep: 1 } } }).

Common situations: Porting raw MongoDB find() queries into SearchFilters; user-supplied JSON filters passed through unvalidated; Date objects (typeof 'object') used as filter values instead of ISO strings or timestamps.

Related errors


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