mem0ai/mem0 · error · Error
Invalid filter key
Error message
Invalid filter key: ${JSON.stringify(key)} What it means
validateFilter() in the Elasticsearch vector store throws when a filter key is not a plain identifier matching ^[a-zA-Z_][a-zA-Z0-9_]*$. Filter keys are interpolated directly into metadata.${key} field paths in ES queries, so keys with dots, brackets, spaces, or special characters could alter query semantics (or enable query injection), and are rejected before reaching Elasticsearch.
Solutions
- Use flat keys that are plain identifiers: { user_id: 'u1' } — no dots, hyphens, or spaces.
- Rename metadata fields at write time (insert payload keys must also be identifiers) so lookups stay valid.
- If filter keys come from user input, sanitize/whitelist them before calling search.
- Avoid wrapping filters in an extra object level; pass the key/value map directly.
Example fix
// before
await es.search(query, 5, { 'user.id': 'u1', 'agent-id': 'a1' });
// after
await es.search(query, 5, { user_id: 'u1', agent_id: 'a1' }); Defensive patterns
Strategy: validation
Validate before calling
const SAFE_KEY = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
const badKeys = Object.keys(filters ?? {}).filter((k) => !SAFE_KEY.test(k));
if (badKeys.length) throw new Error(`Invalid filter keys: ${badKeys.join(', ')}`); Type guard
const SAFE_KEY = /^[a-zA-Z_][a-zA-Z0-9_]*$/; const hasSafeFilterKeys = (f: unknown): f is Record<string, string | number | boolean> => typeof f === 'object' && f !== null && Object.keys(f).every((k) => SAFE_KEY.test(k));
Prevention
- Restrict metadata field names to plain identifiers (letters, digits, underscore, no leading digit) at insert time.
- Never build filter keys from raw user input without whitelisting.
- Keep filters flat — no nested objects or dotted paths.
When it happens
Trigger: Calling search/list/delete with filters whose keys contain dots (e.g. 'user.id'), hyphens ('agent-id'), spaces, leading digits ('2fa'), or when the key is not a string at all. Nested object filter shapes like { metadata: { user_id: 'x' } } also produce invalid keys.
Common situations: Using metadata keys with dots or hyphens that Elasticsearch would otherwise interpret as nested field paths; passing a nested/wrapped filter object instead of a flat one; dynamic filter keys sourced from user input containing arbitrary characters.
Related errors
- Filter value for must be string, number, or boolean
- Invalid filter key
- AND filter value must be a list of filter dicts, got
- AND filter value must be a list of filter dicts, got
- AND filter value must be a list of filter dicts, got
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/d1844bbd7e68a174.
Report an issue: GitHub.
Appendix: source
Thrown at mem0-ts/src/oss/src/vector_stores/elasticsearch.ts:33
password?: string;
collectionName: string;
embeddingModelDims: number;
dimension?: number;
useSsl?: boolean;
caCerts?: string;
verifyCerts?: boolean;
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;View on GitHub (pinned to 001c235229)