mem0ai/mem0 · error · Error
S3 Vectors cannot negate this filter shape.
Error message
S3 Vectors cannot negate this filter shape.
What it means
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.
Source
Thrown at mem0-ts/src/oss/src/vector_stores/s3_vectors.ts:667
return { [key]: operators };
}
private negateFilter(filter: S3Filter): S3Filter {
if (Array.isArray(filter.$and)) {
return {
$or: filter.$and.map((entry: S3Filter) => this.negateFilter(entry)),
};
}
if (Array.isArray(filter.$or)) {
return {
$and: filter.$or.map((entry: S3Filter) => this.negateFilter(entry)),
};
}
const entries = Object.entries(filter);
if (entries.length !== 1) {
throw new Error("S3 Vectors cannot negate this filter shape.");
}
const [key, rawValue] = entries[0];
if (key.startsWith("$")) {
throw new Error("S3 Vectors cannot negate this filter shape.");
}
const value =
typeof rawValue === "object" && rawValue !== null
? rawValue
: { $eq: rawValue };
const negated: Record<string, any> = {};
for (const [operator, operand] of Object.entries(value)) {
switch (operator) {
case "$eq":
negated.$ne = operand;
break;View on GitHub (pinned to 001c235229)
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.
Example fix
// before
filters = { NOT: { user_id: "a", org_id: "b" } } // two fields in one negated object
// after
filters = { AND: [ { user_id: { ne: "a" } }, { org_id: { ne: "b" } } ] } Defensive patterns
Strategy: validation
Validate before calling
function assertSingleFieldLeaves(filters: any): void {
for (const [k, v] of Object.entries(filters ?? {})) {
if (["AND","OR"].includes(k)) { (v as any[]).forEach(assertSingleFieldLeaves); continue; }
if (k === "NOT") { assertSingleFieldLeaves(v); continue; }
if (v && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length > 1)
throw new Error('Each filter leaf must contain exactly one field');
}
}
assertSingleFieldLeaves(filters); Type guard
const isSingleFieldFilter = (f: Record<string, unknown>): boolean => Object.keys(f).length === 1 && !Object.keys(f)[0].startsWith('$'); Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- S3 Vectors does not support '${operator}' metadata filters.
- Unsupported S3 Vectors filter operator: ${operator}
- Top-level entity parameters [${invalidKeys.join(", ")}] are
- Invalid ${name}: cannot be empty or whitespace-only. Provide
- Invalid ${name}: cannot contain whitespace. Provide a valid
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/f071c7fa6d2e2491.
Report an issue: GitHub.