mem0ai/mem0 · error · Error

Unsupported filter operator: ${operator}

Error message

Unsupported filter operator: ${operator}

What it means

The OpenSearch filter builder compiles each operator into a specific query leaf (term, terms, range, wildcard). Unknown operator keys fall to a default case that throws, because no equivalent OpenSearch clause is defined and silently ignoring the operator would return unfiltered (over-broad) results.

Source

Thrown at mem0-ts/src/oss/src/vector_stores/opensearch.ts:618

            range: {
              [this.payloadField(key, false)]: {
                [operator]: operatorValue,
              },
            },
          };
        case "contains":
        case "icontains":
          this.assertScalarValue(key, operatorValue);
          return {
            wildcard: {
              [this.payloadField(key, true)]: {
                value: `*${escapeWildcard(String(operatorValue))}*`,
                case_insensitive: operator === "icontains",
              },
            },
          };
        default:
          throw new Error(`Unsupported filter operator: ${operator}`);
      }
    });

    return clauses.length === 1 ? clauses[0] : { bool: { filter: clauses } };
  }

  private payloadField(key: string, keyword: boolean): string {
    if (key.startsWith("payload.")) {
      return keyword && !key.endsWith(".keyword") ? `${key}.keyword` : key;
    }

    const field = `payload.${key}`;
    return keyword ? `${field}.keyword` : field;
  }

  // Filter values become OpenSearch term/terms/range/wildcard leaves. Allowing
  // an object here lets a caller inject raw query parameters (e.g. a `term`
  // object form with `boost`/`case_insensitive`), so reject non-scalar leaves.

View on GitHub (pinned to 001c235229)

Solutions

  1. Replace unsupported operators: ne -> nin: [value]; endsWith -> contains + client-side check.
  2. Verify operator spelling/casing against the supported list (eq/gt/gte/lt/lte/in/nin/contains/icontains/startsWith and boolean/range forms).
  3. Keep per-backend filter builders instead of one shared schema across stores.

Example fix

// before
filters = { status: { ne: 'deleted' } };

// after
filters = { status: { nin: ['deleted'] } };
Defensive patterns

Strategy: validation

Validate before calling

const OPS = new Set(['in','nin','contains','icontains','startsWith','gt','gte','lt','lte']);
for (const [field, ops] of Object.entries(fieldFilters)) {
  for (const op of Object.keys(ops || {})) {
    if (!OPS.has(op)) throw new TypeError(`Operator '${op}' not supported on field '${field}'`);
  }
}

Type guard

const isOpenSearchOp = (op: string): boolean => OPS.has(op);

Try / catch

catch (e) { if (e.message.includes('Unsupported filter operator')) { /* map to nin/contains and retry */ } }

Prevention

When it happens

Trigger: Using an operator this store does not implement, e.g. { field: { ne: 'x' } }, { field: { endsWith: 'y' } }, or wrong casing like { Contains: 'x' }.

Common situations: Sharing one filter schema across vector stores with different operator sets; typos or casing errors; new operators added to the Python SDK before the TS OpenSearch store.

Related errors


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