mem0ai/mem0 · error · Error

Neptune Analytics cannot negate this filter shape for vector

Error message

Neptune Analytics cannot negate this filter shape for vector search.

What it means

negateVertexFilter() implements De Morgan transformation only for shapes it can invert: andAll/orAll compositions, in, and notIn. If a $not wraps a filter shape it cannot negate (e.g. startsWith, contains, or a combined eq shape with no inverse mapping), it throws rather than emitting a filter with incorrect semantics.

Source

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

    if (filter.lessThanOrEquals) {
      return {
        greaterThan: filter.lessThanOrEquals,
      };
    }

    if (filter.in) {
      return {
        notIn: filter.in,
      };
    }

    if (filter.notIn) {
      return {
        in: filter.notIn,
      };
    }

    throw new Error(
      "Neptune Analytics cannot negate this filter shape for vector search.",
    );
  }

  private buildWhereClause(
    filters?: SearchFilters,
    startIndex: number = 1,
  ): WhereClauseResult {
    const clauses: string[] = [];
    const parameters: Record<string, any> = {};
    let nextIndex = startIndex;

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

      if (key === "$and" || key === "$or") {

View on GitHub (pinned to 001c235229)

Solutions

  1. Move the negation to supported leaves: negate equality ({ field: 'x' } under $not) or membership (in).
  2. Post-filter client-side: fetch with the positive filter removed, then exclude matching payloads in JS.
  3. Restructure: use notIn lists instead of $not over startsWith/contains.

Example fix

// before
store.search(q, 20, { $not: [{ name: { startsWith: 'test-' } }] }); // throws

// after
const res = await store.search(q, 20);
const filtered = res.filter(r => !r.payload?.name?.startsWith('test-'));
Defensive patterns

Strategy: fallback

Validate before calling

function isNegatableShape(node: any): boolean {
  if (Array.isArray(node.$and) || Array.isArray(node.$or)) return node.$and?.every(isNegatableShape) ?? node.$or?.every(isNegatableShape) ?? true;
  const v = Object.values(node)[0];
  if (v && typeof v === 'object' && !Array.isArray(v)) {
    const op = Object.keys(v)[0];
    return op === 'in' || op === 'notIn' || op === 'eq'; // only these have inverses
  }
  return true; // plain equality is negatable
}

Type guard

type NegatableFilter =
  | { in: { property: string; value: (string | number)[] } }
  | { notIn: { property: string; value: (string | number)[] } }
  | { andAll: NegatableFilter[] }
  | { orAll: NegatableFilter[] };

Try / catch

try { results = await store.search(q, k, filters); }
catch (e) {
  if (e instanceof Error && e.message.includes('cannot negate this filter shape')) {
    // fallback: search without the $not clause, exclude matches client-side
    const positive = removeNotClauses(filters);
    const wide = await store.search(q, k, positive);
    results = applyNotClientSide(wide, filters.$not);
  } else throw e;
}

Prevention

When it happens

Trigger: { $not: [{ name: { startsWith: 'test-' } }] } or { $not: [{ data: { contains: 'x' } }] } on the neptune-analytics provider; nested $not inside $and/$or trees where the inner entries hit the unhandled shapes.

Common situations: Exclusion filters for prefix or substring matching; machine-generated filter trees that wrap arbitrary leaves in $not.

Related errors


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