mastra-ai/mastra · error

Invalid filter format: expected a plain object, got ${receiv

Error message

Invalid filter format: expected a plain object, got ${receivedType}

What it means

After parsing (or if the input was already an object), parseFilterValue validates that the resulting filter is a plain object — not null, an array, or a primitive. This error is thrown when the filter resolves to a non-plain-object value, because vector-store metadata filters must be an object of field conditions.

Source

Thrown at packages/rag/src/utils/tool-helpers.ts:195

  let parsedFilter = filter;
  if (typeof filter === 'string') {
    try {
      parsedFilter = JSON.parse(filter);
    } catch (error) {
      if (logger) {
        logger.error('Invalid filter', { filter, error });
      }
      throw new Error(`Invalid filter format: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  // Validate that non-string filter is a plain object
  if (typeof parsedFilter !== 'object' || parsedFilter === null || Array.isArray(parsedFilter)) {
    const receivedType = Array.isArray(parsedFilter) ? 'array' : typeof parsedFilter;
    if (logger) {
      logger.error('Invalid filter', { filter, error: 'Filter must be a plain object' });
    }
    throw new Error(`Invalid filter format: expected a plain object, got ${receivedType}`);
  }

  return parsedFilter as Record<string, any>;
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Wrap conditions in an object, e.g. `{ category: { $in: ['a','b'] } }` instead of an array.
  2. Ensure string filters parse to a JSON object, not an array or scalar.
  3. Guard the call site: check the value is a non-null non-array object before invoking the tool.
  4. Translate list-style intent into the vector store's supported operators ($in/$or).

Example fix

// before
await tool.execute({ filter: ["news", "sports"] });
// after
await tool.execute({ filter: { category: { $in: ["news", "sports"] } } });
Defensive patterns

Strategy: validation

Validate before calling

function isPlainObject(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}
if (!isPlainObject(filter)) throw new TypeError('filter must be a plain object of field conditions');

Type guard

function isPlainObject(v: unknown): v is Record<string, unknown> {
  return Object.prototype.toString.call(v) === '[object Object]';
}

Try / catch

try {
  await tool.execute({ filter });
} catch (e) {
  if ((e as Error).message.includes('expected a plain object')) {
    return tool.execute({ filter: normalizeFilter(filter) }); // wrap arrays via $in, drop null
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing `filter` as an array (e.g. `["a","b"]` or `[{...}]`), a JSON string like `"null"`, `"[...]"` or `"42"`, or a value like a number/boolean that parsed successfully but is not an object.

Common situations: LLM tool calls emitting JSON arrays as filters; users passing list-style conditions expecting OR semantics; configs where a filter variable was accidentally set to null/undefined-as-string.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/3d940d60c347f56c. Report an issue: GitHub.