mem0ai/mem0 · error · Error
Oracle filter operator '${operator}' requires a string value
Error message
Oracle filter operator '${operator}' requires a string value What it means
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.
Source
Thrown at mem0-ts/src/oss/src/vector_stores/oracledb.ts:223
}
const [variable, passing] = bindFilterValue(item, binds);
variables.push(variable);
listPassings.push(passing);
}
const membership = jsonExists(
path,
`@ in (${variables.join(", ")})`,
listPassings,
);
additionalClauses.push(
operator === "in" ? membership : `NOT (${membership})`,
);
continue;
}
if (typeof operand !== "string") {
throw new Error(
`Oracle filter operator '${operator}' requires a string value`,
);
}
if (operator === "contains") {
const [variable, passing] = bindFilterValue(operand, binds);
predicates.push(`@ has substring ${variable}`);
passings.push(passing);
} else {
const [variable, passing] = bindFilterValue(operand.toLowerCase(), binds);
predicates.push(`@.lower() has substring ${variable}`);
passings.push(passing);
}
}
const clauses = [...additionalClauses];
if (predicates.length > 0) {
clauses.unshift(jsonExists(path, predicates.join(" && "), passings));View on GitHub (pinned to 001c235229)
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.
Example fix
// before
filters = { name: { contains: searchTerm } }; // searchTerm may be a number
// after
filters = { name: { contains: String(searchTerm) } }; Defensive patterns
Strategy: validation
Validate before calling
function coerceContains(filters: any): any {
if (Array.isArray(filters)) return filters.map(coerceContains);
if (!filters || typeof filters !== 'object') return filters;
const out: Record<string, any> = {};
for (const [k, v] of Object.entries(filters)) {
if (v && typeof v === 'object' && !Array.isArray(v) && ('contains' in v || 'icontains' in v)) {
const ops: Record<string, any> = {};
for (const [op, val] of Object.entries(v)) {
if (op === 'contains' || op === 'icontains') {
if (val === null || val === undefined) continue;
ops[op] = typeof val === 'string' ? val : String(val);
} else ops[op] = val;
}
if (Object.keys(ops).length) out[k] = ops;
} else out[k] = v;
}
return out;
} Type guard
const isStringFilter = (v: unknown): v is string => typeof v === 'string';
Prevention
- 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.
When it happens
Trigger: { name: { contains: 42 } } (number), { name: { icontains: ['a','b'] } } (array), { name: { contains: null } } — null is typeof 'object' and also fails the string check.
Common situations: 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.
Related errors
- Unsupported Oracle filter operator(s) for field '${metadataK
- Oracle filter operator '${operator}' requires a scalar value
- Oracle filter operator '${operator}' does not support null
- Oracle filter operator '${operator}' requires a non-empty ar
- Oracle filter operator '${operator}' requires scalar values
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/6400edcb84451db8.
Report an issue: GitHub.