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

  1. Omit the filter argument entirely to query all indexed series: client.queryLabels() with no filter.
  2. If you compute the list dynamically, fall back to undefined when it is empty: filter: list.length ? list : undefined.
  3. 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

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


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)