mem0ai/mem0 · error · Error
Filter value for ${JSON.stringify(key)} must be string, numb
Error message
Filter value for ${JSON.stringify(key)} must be string, number, or boolean What it means
validateFilter() in the Elasticsearch vector store throws when a filter value is not a string, number, or boolean. Non-scalar values (objects, arrays, null, undefined) cannot be mapped onto ES term queries safely, so they are rejected client-side before the query is built.
Source
Thrown at mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:40
autoCreateIndex?: boolean;
headers?: Record<string, string>;
}
const SAFE_FILTER_KEY = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
// Mirrors the Python provider's _validate_filter. Filter keys are interpolated
// into `metadata.${key}` field paths, so reject anything that is not a plain
// identifier and reject non-scalar values before they reach the query.
function validateFilter(key: string, value: unknown): void {
if (typeof key !== "string" || !SAFE_FILTER_KEY.test(key)) {
throw new Error(`Invalid filter key: ${JSON.stringify(key)}`);
}
if (
typeof value !== "string" &&
typeof value !== "number" &&
typeof value !== "boolean"
) {
throw new Error(
`Filter value for ${JSON.stringify(key)} must be string, number, or boolean`,
);
}
}
export class ElasticsearchDB implements VectorStore {
private client!: Client;
private readonly config: ElasticsearchConfig;
private readonly collectionName: string;
private readonly dimension: number;
private readonly autoCreateIndex: boolean;
private _initPromise?: Promise<void>;
constructor(config: ElasticsearchConfig) {
this.config = config;
this.collectionName = config.collectionName;
this.dimension = config.dimension || config.embeddingModelDims || 1536;
this.autoCreateIndex = config.autoCreateIndex !== false;View on GitHub (pinned to 001c235229)
Solutions
- Replace array values with an $or branch per value, or store a single scalar field per tag.
- Strip undefined/null entries from filter objects before calling search (Object.fromEntries(Object.entries(f).filter(([,v]) => v != null))).
- Flatten nested objects into dot-free scalar fields at insert time and filter on those.
Example fix
// before
await es.search(query, 5, { tags: ['red', 'blue'], user_id: undefined });
// after
await es.search(query, 5, {
$or: [{ tags: 'red' }, { tags: 'blue' }],
}); Defensive patterns
Strategy: validation
Validate before calling
const isScalar = (v: unknown): v is string | number | boolean =>
['string', 'number', 'boolean'].includes(typeof v);
const invalid = Object.entries(filters ?? {}).filter(([, v]) => !isScalar(v));
if (invalid.length) {
throw new Error(`Non-scalar filter values for keys: ${invalid.map(([k]) => k).join(', ')}`);
} Type guard
type Scalar = string | number | boolean; const isScalarFilterMap = (f: unknown): f is Record<string, Scalar> => typeof f === 'object' && f !== null && Object.values(f).every((v) => v === null || ['string', 'number', 'boolean'].includes(typeof v));
Prevention
- Strip null/undefined entries from filter objects before calling search.
- Express multi-value matching with $or branches, not arrays as values.
- Flatten structured metadata into scalar fields at insert time.
When it happens
Trigger: Calling search/list with a filter value that is an array (e.g. { tags: ['a','b'] }), an object ({ user: { id: 1 } }), null, or undefined.
Common situations: Expecting OR-semantics by passing an array of values (not supported here — use $or branches instead); undefined values leaking in from optional fields spread into filters ({...req.query} where a param is absent); null values from JSON configs.
Related errors
- Invalid filter key: ${JSON.stringify(key)}
- Top-level entity parameters [${invalidKeys.join(", ")}] are
- Invalid ${name}: cannot be empty or whitespace-only. Provide
- Invalid ${name}: cannot contain whitespace. Provide a valid
- AND filter value must be a list of filter dicts, got ${typeo
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/3e88ef51c32ff5d3.
Report an issue: GitHub.