{"record":{"id":"38b8d1bbfcda2285","repo":"mem0ai/mem0","slug":"key-filter-requires-a-non-empty-list","errorCode":null,"errorMessage":"${key} filter requires a non-empty list","messagePattern":"(.+?) 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":498,"sourceCode":"      payload: this.normalizeMetadata(vector.metadata),\n    };\n  }\n\n  private convertFilters(filters?: SearchFilters): S3Filter | undefined {\n    if (!filters || Object.keys(filters).length === 0) {\n      return undefined;\n    }\n    const normalized = this.convertFilterNode(filters);\n    return this.isNoOpFilter(normalized) ? undefined : normalized;\n  }\n\n  private convertFilterNode(node: Record<string, any>): S3Filter {\n    const clauses: S3Filter[] = [];\n\n    for (const [key, value] of Object.entries(node)) {\n      if (key === \"$and\" || key === \"$or\") {\n        if (!Array.isArray(value) || value.length === 0) {\n          throw new Error(`${key} filter requires a non-empty list`);\n        }\n        const normalizedEntries = value.map((entry) =>\n          this.convertFilterNode(entry),\n        );\n        const entries = normalizedEntries.filter(\n          (entry) => !this.isNoOpFilter(entry),\n        );\n        if (key === \"$and\") {\n          if (entries.some((entry) => this.isAlwaysFalseFilter(entry))) {\n            return { [ALWAYS_FALSE_FILTER_KEY]: true };\n          }\n          if (entries.length === 0) {\n            continue;\n          }\n        } else {\n          if (normalizedEntries.some((entry) => this.isNoOpFilter(entry))) {\n            continue;\n          }","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/vector_stores/s3_vectors.ts#L480-L516","documentation":"When converting mem0 filters to S3 Vectors filter syntax, $and and $or must map to a non-empty array of sub-filter objects. An empty array (or non-array) throws, because S3 Vectors has no representation for a vacuous conjunction/disjunction and the store refuses to guess semantics.","triggerScenarios":"filters: { $and: [] }, { $or: [] }, or dynamically built filters where the conditions array ended up empty ({ $and: conds } when conds is []).","commonSituations":"Programmatic filter building that spreads possibly-empty lists; user-supplied advanced_filters JSON with empty arrays; refactors that strip conditions but keep the wrapper.","solutions":["Omit the $and/$or key entirely when the conditions list is empty","Guard when building: const filters = conds.length ? { $and: conds } : undefined","Validate incoming filter JSON rejects empty logical arrays at the API boundary"],"exampleFix":"// before\nconst filters = { $and: conditions }; // conditions may be []\n\n// after\nconst filters = conditions.length > 0 ? { $and: conditions } : undefined;\nconst r = filters ? await vs.search(vec, { filters }) : await vs.search(vec, {});","handlingStrategy":"validation","validationCode":"function pruneEmptyLogical(filters: any): any {\n  const out: any = {};\n  for (const [k, v] of Object.entries(filters ?? {})) {\n    if ((k === '$and' || k === '$or') && Array.isArray(v) && v.length === 0) continue;\n    if (k === '$not' && Array.isArray(v) && v.length === 0) continue;\n    out[k] = v;\n  }\n  return Object.keys(out).length ? out : undefined;\n}\nconst safeFilters = pruneEmptyLogical(filters);","typeGuard":"const isNonEmptyFilterList = (v: unknown, key: string): v is Record<string, any>[] =>\n  (key === '$and' || key === '$or' || key === '$not') && Array.isArray(v) && v.length > 0;","tryCatchPattern":"try { await vs.search(vec, { filters }); } catch (e) { if (e instanceof Error && e.message.includes('requires a non-empty list')) { /* drop empty $and/$or, retry */ } throw e; }","preventionTips":["Build logical filters conditionally so empty wrappers are never emitted","Validate external filter JSON with zod: z.array(z.record(...)).min(1) for $and/$or","Unit-test filter builders with zero-condition inputs"],"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-15T22:17:37.221Z"}