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

  1. Replace array values with an $or branch per value, or store a single scalar field per tag.
  2. Strip undefined/null entries from filter objects before calling search (Object.fromEntries(Object.entries(f).filter(([,v]) => v != null))).
  3. 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

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


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