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
- Wrap conditions in an object, e.g. `{ category: { $in: ['a','b'] } }` instead of an array.
- Ensure string filters parse to a JSON object, not an array or scalar.
- Guard the call site: check the value is a non-null non-array object before invoking the tool.
- 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
- Express list conditions with $in/$or objects, never arrays.
- Type the filter parameter as Record<string, unknown> so TS rejects arrays/scalars.
- Normalize incoming (LLM-supplied) filters through a shape validator before execution.
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
- validation.messages.join(', ')
- HTML chunking requires either headers or sections to be spec
- JSON chunking requires maxSize to be specified
- Sentence chunking requires maxSize to be specified
- Keywords must be greater than 0
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/3d940d60c347f56c.
Report an issue: GitHub.