redis/node-redis · error · Error
FILTER was given an empty filter expression list; omit it…
Error message
FILTER was given an empty filter expression list; omit it to query all indexed series
What it means
parseQueryLabelsFilterArgument throws when a caller passes an empty array for the FILTER argument of TS.QUERYLABELS / TS.QUERYLABELS_VALUES. The library treats filter=[] as a local usage error rather than silently widening the query to all indexed series — the server also rejects a bare FILTER token with no expressions. To query every indexed series, omit the filter argument entirely. Expressions are sent verbatim (not parsed, reordered, or deduplicated).
Solutions
- Omit the filter argument entirely to query all indexed series: client.queryLabels() with no filter.
- If you compute the list dynamically, fall back to undefined when it is empty: filter: list.length ? list : undefined.
- If you intend to filter, ensure at least one expression such as 'label=value' is present before calling.
Example fix
// before — empty array is a usage error
await client.queryLabels({ filter: expressions }); // expressions === []
// after — omit filter when empty to query all series
await client.queryLabels({
filter: expressions.length ? expressions : undefined
}); Defensive patterns
Strategy: type-guard
Validate before calling
import type { RedisVariadicArgument } from '@redis/client';
function normalizeFilter(filter?: RedisVariadicArgument): RedisVariadicArgument | undefined {
if (filter === undefined) return undefined;
const arr = Array.isArray(filter) ? filter : [filter];
return arr.length > 0 ? filter : undefined; // empty -> omit to query all series
}
// usage:
await client.queryLabels({ filter: normalizeFilter(expressions) }); Type guard
const isNonEmptyFilter = (f: unknown): f is string | [string, ...string[]] => typeof f === 'string' || (Array.isArray(f) && f.length > 0 && f.every(x => typeof x === 'string'));
Prevention
- Remember the contract: omit filter to query all series; never pass [].
- When building filter lists dynamically, coerce [] to undefined at the call site.
- Add a type guard so empty arrays never reach parseQueryLabelsFilterArgument.
- Document on the command's JSDoc that filter is optional and must be non-empty if provided.
When it happens
Trigger: Calling client.queryLabels({ filter: [] }) or client.queryLabelsValues({ filter: [] }); or building the filter list dynamically such that it ends up empty (e.g., a label-matching step returned no expressions).
Common situations: A filter-expression builder returns [] when no labels match a condition; copy-paste from another command where an empty array was tolerated; assuming [] means 'no filter' (it does not — undefined does).
Related errors
- Reconnect strategy should return `false | Error | number`…
- All statistics values must be non-negative
- "arguments[ ]" must be of type "string | Buffer", got…
- Cannot split : key region does not align with keystep
- Cannot split : key region overruns the arguments
AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11).
Data as JSON: /api/errors/8c6c29d8766f59f4.
Report an issue: GitHub.
Appendix: source
Thrown at packages/time-series/lib/commands/helpers.ts:338
parser.push('SELECTED_LABELS');
parser.pushVariadic(selectedLabels);
}
/**
* Pushes the optional `FILTER filterExpr [filterExpr ...]` tail shared by the
* `TS.QUERYLABELS` forms. Omitting `filter` queries all indexed series; passing
* an explicitly empty array is a local usage error rather than a silent widen to
* all series (the server also rejects a bare `FILTER` token). Expressions are
* sent verbatim — not parsed, reordered, or deduplicated.
*/
export function parseQueryLabelsFilterArgument(
parser: CommandParser,
filter?: RedisVariadicArgument
) {
if (filter === undefined) return;
if (Array.isArray(filter) && filter.length === 0) {
throw new Error('FILTER was given an empty filter expression list; omit it to query all indexed series');
}
parser.push('FILTER');
parser.pushVariadic(filter);
}
export type RawLabelValue = BlobStringReply | NullReply;
export type RawLabels<T extends RawLabelValue> = ArrayReply<TuplesReply<[
label: BlobStringReply,
value: T
]>>;
export function transformRESP2Labels<T extends RawLabelValue>(
labels: RawLabels<T>,
typeMapping?: TypeMapping
): MapReply<BlobStringReply, T> {
const unwrappedLabels = labels as unknown as UnwrapReply<typeof labels>;View on GitHub (pinned to 90fd0652bc)