{"record":{"id":"6400edcb84451db8","repo":"mem0ai/mem0","slug":"oracle-filter-operator-operator-requires-a-st","errorCode":null,"errorMessage":"Oracle filter operator '${operator}' requires a string value","messagePattern":"Oracle filter operator '(.+?)' requires a string value","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mem0-ts/src/oss/src/vector_stores/oracledb.ts","lineNumber":223,"sourceCode":"        }\n        const [variable, passing] = bindFilterValue(item, binds);\n        variables.push(variable);\n        listPassings.push(passing);\n      }\n\n      const membership = jsonExists(\n        path,\n        `@ in (${variables.join(\", \")})`,\n        listPassings,\n      );\n      additionalClauses.push(\n        operator === \"in\" ? membership : `NOT (${membership})`,\n      );\n      continue;\n    }\n\n    if (typeof operand !== \"string\") {\n      throw new Error(\n        `Oracle filter operator '${operator}' requires a string value`,\n      );\n    }\n\n    if (operator === \"contains\") {\n      const [variable, passing] = bindFilterValue(operand, binds);\n      predicates.push(`@ has substring ${variable}`);\n      passings.push(passing);\n    } else {\n      const [variable, passing] = bindFilterValue(operand.toLowerCase(), binds);\n      predicates.push(`@.lower() has substring ${variable}`);\n      passings.push(passing);\n    }\n  }\n\n  const clauses = [...additionalClauses];\n  if (predicates.length > 0) {\n    clauses.unshift(jsonExists(path, predicates.join(\" && \"), passings));","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/vector_stores/oracledb.ts#L205-L241","documentation":"The remaining string operators, contains and icontains, compile to '@ has substring' / '@.lower() has substring' JSON path predicates, which only work on string bind values. After eq/ne/in/nin are handled, any other operator reaching the string branch with a non-string operand is rejected.","triggerScenarios":"{ name: { contains: 42 } } (number), { name: { icontains: ['a','b'] } } (array), { name: { contains: null } } — null is typeof 'object' and also fails the string check.","commonSituations":"Template-driven filter builders that concatenate whatever value the client sent into a contains clause; auto-complete/search boxes that submit numbers; forgetting to String() a numeric search term.","solutions":["Convert the operand to a string: { name: { contains: String(term) } }.","Use eq/in for exact matching of numbers instead of contains.","Reject or coerce non-string inputs at the API boundary before building filters.","For icontains, still pass a string; lowercasing of both sides is done by the adapter."],"exampleFix":"// before\nfilters = { name: { contains: searchTerm } }; // searchTerm may be a number\n\n// after\nfilters = { name: { contains: String(searchTerm) } };","handlingStrategy":"validation","validationCode":"function coerceContains(filters: any): any {\n  if (Array.isArray(filters)) return filters.map(coerceContains);\n  if (!filters || typeof filters !== 'object') return filters;\n  const out: Record<string, any> = {};\n  for (const [k, v] of Object.entries(filters)) {\n    if (v && typeof v === 'object' && !Array.isArray(v) && ('contains' in v || 'icontains' in v)) {\n      const ops: Record<string, any> = {};\n      for (const [op, val] of Object.entries(v)) {\n        if (op === 'contains' || op === 'icontains') {\n          if (val === null || val === undefined) continue;\n          ops[op] = typeof val === 'string' ? val : String(val);\n        } else ops[op] = val;\n      }\n      if (Object.keys(ops).length) out[k] = ops;\n    } else out[k] = v;\n  }\n  return out;\n}","typeGuard":"const isStringFilter = (v: unknown): v is string => typeof v === 'string';","tryCatchPattern":null,"preventionTips":["String() search-box input before putting it into contains/icontains.","Use eq/in for numeric equality, never contains.","Reject null contains operands in your filter builder."],"tags":["oracle","filters","strings","validation"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}