n8n-io/n8n · error
Filter operator "${operator}" on key "${key}" requires a non
Error message
Filter operator "${operator}" on key "${key}" requires a non-empty array value. What it means
Thrown by buildConditionClause in the Postgres backend when an `in`/`nin` filter condition's value is not an array or is empty. The `in`/`nin` operators compile to per-candidate jsonb containment OR-clauses, which are meaningless for zero candidates, so the value is rejected before SQL is built. Operator and key are named in the message.
Source
Thrown at packages/@n8n/agents/src/vector-stores/postgres.ts:191
private buildConditionClause(
condition: FilterCondition,
nextParam: (value: unknown) => string,
): string {
const { key, operator, value } = condition;
switch (operator) {
case 'eq': {
const v = nextParam(JSON.stringify({ [key]: value }));
return `metadata @> ${v}::jsonb`;
}
case 'ne': {
const v = nextParam(JSON.stringify({ [key]: value }));
return `NOT (metadata @> ${v}::jsonb)`;
}
case 'in':
case 'nin': {
if (!Array.isArray(value) || value.length === 0) {
throw new Error(
`Filter operator "${operator}" on key "${key}" requires a non-empty array value.`,
);
}
// Containment per candidate (rather than `metadata->>key = ANY(text[])`) keeps
// numeric and string metadata distinct — text extraction would otherwise make
// the number 5 and the string "5" match the same filter value.
const anyMatch = value
.map(
(candidate) => `metadata @> ${nextParam(JSON.stringify({ [key]: candidate }))}::jsonb`,
)
.join(' OR ');
return operator === 'in' ? `(${anyMatch})` : `NOT (${anyMatch})`;
}
default:
throw new Error(`Unsupported filter operator: "${String(operator)}"`);
}
}
View on GitHub (pinned to 5ac6606e81)
Solutions
- Drop the `in`/`nin` condition from `conditions` when the array is empty.
- Build conditions defensively: `if (values.length) conditions.push({ key, operator: 'in', value: values })`.
- If all conditions drop out, omit the filter entirely so the query is unfiltered.
Example fix
// before
await store.search('q', {
filter: { conditions: [{ key: 'status', operator: 'in', value: [] }] },
});
// after
const conditions = [];
if (statuses.length > 0) {
conditions.push({ key: 'status', operator: 'in', value: statuses });
}
await store.search('q',
conditions.length ? { filter: { conditions, combineWith: 'and' } } : undefined,
); Defensive patterns
Strategy: validation
Validate before calling
function dropEmptyInConditions(filter: VectorFilter): VectorFilter | undefined {
const conditions = filter.conditions.filter(
(c) => !(c.operator === 'in' || c.operator === 'nin') || (Array.isArray(c.value) && c.value.length > 0),
);
return conditions.length > 0 ? { ...filter, conditions } : undefined;
}
const f = dropEmptyInConditions(inputFilter);
await store.search('q', f ? { filter: f } : undefined); Type guard
function isInCondition(c: FilterCondition): c is FilterCondition & { operator: 'in' | 'nin'; value: (string | number)[] } {
return (c.operator === 'in' || c.operator === 'nin') && Array.isArray(c.value) && c.value.length > 0;
} Prevention
- Only add an `in`/`nin` condition when the value array has at least one element.
- If filtering empties all conditions, run an unfiltered query instead.
- Centralize filter construction so the empty-array rule is enforced in one place.
When it happens
Trigger: Searching with `{ filter: { conditions: [{ key: 'status', operator: 'in', value: [] }] } }`; passing a scalar to an `in` condition; a dynamic filter whose candidate list was emptied by upstream filtering.
Common situations: A UI multi-select where the user clears all options but the filter is still constructed; a default empty-array filter; set difference/remove operations leaving no candidates.
Related errors
- Invalid filter operator "${operator}" for key "${key}". Supp
- Filter operator "${operator}" on key "${key}" requires a non
- Filter operator "${operator}" on key "${key}" requires array
- Filter operator "${operator}" on key "${key}" requires a str
- filterableKeys must contain at least one key
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/467a6d1f2bacb7b0.
Report an issue: GitHub.