mem0ai/mem0 · error · Error

Cannot mix range operators

Error message

Cannot mix range operators (${ops.filter((o) => rangeOps.includes(o)).join(", ")}) with non-range operators (${nonRangeOps.join(", ")}) for field '${key}'. Use AND to combine them as separate conditions.

What it means

In the Qdrant store, an object-valued filter field is converted into a single Qdrant range condition when it contains range operators (gt, gte, lt, lte). Qdrant's range model allows only one range object per field, so mixing range operators with non-range operators (eq, ne, in, nin, contains, ...) in the same object is rejected with guidance to split them into separate AND conditions.

Solutions

  1. Split the mixed condition into separate filter dicts combined with AND: { AND: [ { ts: { gte: 1 } }, { ts: { eq: 5 } } ] }
  2. Keep pure range objects ({ gte, lte }) per field and put equality checks as their own top-level filter entries

Example fix

// before
const r = await vs.search(vec, { filters: { ts: { gte: 1, eq: 5 } } });

// after
const r = await vs.search(vec, {
  filters: { AND: [ { ts: { gte: 1 } }, { ts: { eq: 5 } } ] },
});
Defensive patterns

Strategy: validation

Validate before calling

const RANGE_OPS = new Set(['gt','gte','lt','lte']);
function splitMixedRangeFilters(filters: any): any {
  const out: any = {};
  for (const [k, v] of Object.entries(filters)) {
    if (v && typeof v === 'object' && !Array.isArray(v)) {
      const ops = Object.keys(v);
      if (ops.some(o => RANGE_OPS.has(o)) && ops.some(o => !RANGE_OPS.has(o))) {
        const range: any = {}, eqs: any[] = [];
        for (const [op, val] of Object.entries(v)) {
          if (RANGE_OPS.has(op)) range[op] = val; else eqs.push({ [k]: { [op]: val } });
        }
        out.AND = [...(out.AND ?? []), { [k]: range }, ...eqs];
        continue;
      }
    }
    out[k] = v;
  }
  return out;
}

Type guard

const isPureRangeObject = (v: Record<string, any>): boolean =>
  Object.keys(v).length > 0 && Object.keys(v).every(o => ['gt','gte','lt','lte'].includes(o));

Try / catch

try { await vs.search(vec, { filters }); } catch (e) { if (e instanceof Error && e.message.includes('Cannot mix range operators')) { /* split field into AND entries, retry */ } throw e; }

Prevention

When it happens

Trigger: Filters like { ts: { gte: 1, eq: 5 } } or { price: { lt: 100, currency: 'USD' } } — any object with both a range op (gt/gte/lt/lte) and a non-range op under the same key.

Common situations: Naturally writing a compound predicate on one field ({ date: { gte: a, lte: b, ne: holiday } }); merging filter objects from multiple sources that collapse into a single key.

Related errors


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

Appendix: source

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

      }
      // Simple equality
      return { key, match: { value } };
    }

    // Handle array shorthand: {"field": ["a", "b"]} treated as "in" operator
    if (Array.isArray(value)) {
      return { key, match: { any: value } };
    }

    const ops = Object.keys(value);
    const rangeOps = ["gt", "gte", "lt", "lte"];
    const hasRangeOps = ops.some((op) => rangeOps.includes(op));
    const nonRangeOps = ops.filter((op) => !rangeOps.includes(op));

    // Handle range operators
    if (hasRangeOps) {
      if (nonRangeOps.length > 0) {
        throw new Error(
          `Cannot mix range operators (${ops.filter((o) => rangeOps.includes(o)).join(", ")}) ` +
            `with non-range operators (${nonRangeOps.join(", ")}) for field '${key}'. ` +
            `Use AND to combine them as separate conditions.`,
        );
      }
      const range: Record<string, number | string> = {};
      for (const op of rangeOps) {
        if (op in value) {
          range[op] = value[op];
        }
      }
      return { key, range };
    }

    // Handle comparison operators
    if ("eq" in value) {
      return { key, match: { value: value.eq } };
    }

View on GitHub (pinned to 001c235229)