{"record":{"id":"5b2b3fb0f12abc64","repo":"mem0ai/mem0","slug":"not-filter-requires-a-non-empty-list","errorCode":null,"errorMessage":"$not filter requires a non-empty list","messagePattern":"\\$not filter requires a non-empty list","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mem0-ts/src/oss/src/vector_stores/s3_vectors.ts","lineNumber":536,"sourceCode":"            (entry) => !this.isAlwaysFalseFilter(entry),\n          );\n          if (viableEntries.length === 0) {\n            return { [ALWAYS_FALSE_FILTER_KEY]: true };\n          }\n          clauses.push(\n            viableEntries.length === 1\n              ? viableEntries[0]\n              : { [key]: viableEntries },\n          );\n          continue;\n        }\n        clauses.push(entries.length === 1 ? entries[0] : { [key]: entries });\n        continue;\n      }\n\n      if (key === \"$not\") {\n        if (!Array.isArray(value) || value.length === 0) {\n          throw new Error(\"$not filter requires a non-empty list\");\n        }\n\n        const normalizedEntries = value.map((entry) =>\n          this.convertFilterNode(entry),\n        );\n        if (normalizedEntries.some((entry) => this.isNoOpFilter(entry))) {\n          return { [ALWAYS_FALSE_FILTER_KEY]: true };\n        }\n        const negated = normalizedEntries.filter(\n          (entry) => !this.isAlwaysFalseFilter(entry),\n        );\n        if (negated.length === 0) {\n          continue;\n        }\n        const negatedFilters = negated.map((entry) => this.negateFilter(entry));\n        clauses.push(\n          negatedFilters.length === 1\n            ? negatedFilters[0]","sourceCodeStart":518,"sourceCodeEnd":554,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L518-L554","documentation":"$not in S3 Vectors filters must be a non-empty array of sub-filter objects. An empty or non-array value throws before translation, since negating nothing has no defined behavior in the store's filter model.","triggerScenarios":"filters: { $not: [] }, { $not: null }, or { $not: excludeConditions } where excludeConditions was filtered down to nothing.","commonSituations":"Exclusion lists computed at runtime that occasionally end up empty; merging user filter JSON with optional exclusion arrays; copy-paste of $and-style wrappers where empties slipped through.","solutions":["Drop the $not key when the exclusion list is empty","Build conditionally: const filters = exclusions.length ? { $not: exclusions } : {}","Reject empty $not arrays when validating external filter payloads"],"exampleFix":"// before\nconst filters = { $not: excludeTags.map(t => ({ tag: { eq: t } })) };\n\n// after\nconst excl = excludeTags.map(t => ({ tag: { eq: t } }));\nconst filters = excl.length > 0 ? { $not: excl } : {};","handlingStrategy":"validation","validationCode":"function buildNotFilter(exclusions: any[]): any | undefined {\n  const list = exclusions.filter(e => e && typeof e === 'object');\n  return list.length > 0 ? { $not: list } : undefined;\n}\nconst filters = buildNotFilter(excludeList) ?? {};","typeGuard":"const isNonEmptyNotList = (v: unknown): v is Record<string, any>[] =>\n  Array.isArray(v) && v.length > 0 && v.every(i => i && typeof i === 'object' && !Array.isArray(i));","tryCatchPattern":"try { await vs.search(vec, { filters }); } catch (e) { if (e instanceof Error && e.message === '$not filter requires a non-empty list') { /* omit $not, retry */ } throw e; }","preventionTips":["Treat an empty exclusion list as 'no filter', not as an empty $not wrapper","Compose exclusion filters only after filtering out null/undefined entries","Cover empty-exclusion cases in filter-builder tests"],"tags":["s3-vectors","search-filters","logical-operators","validation","typescript"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}