mem0ai/mem0 · error · Error

Invalid filter key: ${JSON.stringify(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.

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)

Solutions

  1. Use flat keys that are plain identifiers: { user_id: 'u1' } — no dots, hyphens, or spaces.
  2. Rename metadata fields at write time (insert payload keys must also be identifiers) so lookups stay valid.
  3. If filter keys come from user input, sanitize/whitelist them before calling search.
  4. 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

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


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/d1844bbd7e68a174. Report an issue: GitHub.