{"record":{"id":"f071c7fa6d2e2491","repo":"mem0ai/mem0","slug":"s3-vectors-cannot-negate-this-filter-shape","errorCode":null,"errorMessage":"S3 Vectors cannot negate this filter shape.","messagePattern":"S3 Vectors cannot negate this filter shape\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mem0-ts/src/oss/src/vector_stores/s3_vectors.ts","lineNumber":667,"sourceCode":"    return { [key]: operators };\n  }\n\n  private negateFilter(filter: S3Filter): S3Filter {\n    if (Array.isArray(filter.$and)) {\n      return {\n        $or: filter.$and.map((entry: S3Filter) => this.negateFilter(entry)),\n      };\n    }\n\n    if (Array.isArray(filter.$or)) {\n      return {\n        $and: filter.$or.map((entry: S3Filter) => this.negateFilter(entry)),\n      };\n    }\n\n    const entries = Object.entries(filter);\n    if (entries.length !== 1) {\n      throw new Error(\"S3 Vectors cannot negate this filter shape.\");\n    }\n\n    const [key, rawValue] = entries[0];\n    if (key.startsWith(\"$\")) {\n      throw new Error(\"S3 Vectors cannot negate this filter shape.\");\n    }\n\n    const value =\n      typeof rawValue === \"object\" && rawValue !== null\n        ? rawValue\n        : { $eq: rawValue };\n    const negated: Record<string, any> = {};\n\n    for (const [operator, operand] of Object.entries(value)) {\n      switch (operator) {\n        case \"$eq\":\n          negated.$ne = operand;\n          break;","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L649-L685","documentation":"When negating a filter (a NOT condition) for S3 Vectors, the negation helper requires a single-field filter object like { field: { $eq: value } }. It throws when the filter object has zero or multiple top-level entries, because De Morgan-style negation over a compound single-object shape is not representable. Multi-clause shapes must be expressed via explicit $and/$or arrays, which are handled by earlier branches.","triggerScenarios":"A filters object that negates into a plain object with more than one key, e.g. NOT over { {a:1}, {b:2} } collapsed into one object instead of an $and array; or an empty filter object {} reaching negateFilter via a NOT construct.","commonSituations":"Complex nested filters combining AND/OR/NOT produced by LLM filter extraction or generic filter builders; filters that worked on other stores but structurally collapse into multi-key objects when converted for S3 Vectors.","solutions":["Express multi-condition filters using explicit AND/OR lists so each negated entry contains exactly one field.","Simplify the filter: split the NOT into pre-negated operators (use ne/nin directly instead of negating eq/in).","Move complex negation client-side: query without the NOT clause and post-filter results locally."],"exampleFix":"// before\nfilters = { NOT: { user_id: \"a\", org_id: \"b\" } } // two fields in one negated object\n\n// after\nfilters = { AND: [ { user_id: { ne: \"a\" } }, { org_id: { ne: \"b\" } } ] }","handlingStrategy":"validation","validationCode":"function assertSingleFieldLeaves(filters: any): void {\n  for (const [k, v] of Object.entries(filters ?? {})) {\n    if ([\"AND\",\"OR\"].includes(k)) { (v as any[]).forEach(assertSingleFieldLeaves); continue; }\n    if (k === \"NOT\") { assertSingleFieldLeaves(v); continue; }\n    if (v && typeof v === \"object\" && !Array.isArray(v) && Object.keys(v).length > 1)\n      throw new Error('Each filter leaf must contain exactly one field');\n  }\n}\nassertSingleFieldLeaves(filters);","typeGuard":"const isSingleFieldFilter = (f: Record<string, unknown>): boolean => Object.keys(f).length === 1 && !Object.keys(f)[0].startsWith('$');","tryCatchPattern":"try { await memory.search(q, { filters }); } catch (e) { if (e instanceof Error && e.message === 'S3 Vectors cannot negate this filter shape.') { /* split NOT into AND of ne/nin */ } else throw e; }","preventionTips":["Express multi-field conditions as AND arrays, not multi-key objects","Prefer explicit ne/nin over NOT","Test complex filter shapes in CI against every store backend you use"],"tags":["s3-vectors","filters","negation","typescript"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}