redis/redis-py · error · DataError

filters cannot be an empty collection; pass None to query al

Error message

filters cannot be an empty collection; pass None to query all indexed series.

What it means

Raised by TS.QUERYLABELS parameter building (_append_filter_expressions) when filters is passed as an explicitly empty collection. None means 'query all series' (FILTER omitted); an empty collection is ambiguous and would silently widen the query, so the library treats it as a usage error. Pass None to query everything, or a non-empty list of filter expressions.

Source

Thrown at redis/commands/timeseries/commands.py:1926

    @staticmethod
    def _append_filter_expressions(
        params: list[EncodableT], filters: Iterable[str] | None
    ):
        """Append the optional FILTER clause for TS.QUERYLABELS.

        ``None`` omits ``FILTER`` entirely (the documented all-series query);
        an explicitly empty collection is a local usage error, since silently
        widening to all series would be surprising. Any iterable is accepted and
        materialized once so single-pass iterators are handled correctly.
        Expressions are passed through verbatim, without parsing, reordering, or
        normalizing.
        """
        if filters is None:
            return
        filters = list(filters)
        if not filters:
            raise DataError(
                "filters cannot be an empty collection; pass None to query "
                "all indexed series."
            )
        params.append("FILTER")
        params.extend(filters)

    @staticmethod
    def _append_uncompressed(params: list[EncodableT], uncompressed: bool | None):
        """Append UNCOMPRESSED tag to params."""
        if uncompressed:
            params.extend(["ENCODING", "UNCOMPRESSED"])

    @staticmethod
    def _append_with_labels(
        params: list[EncodableT],
        with_labels: bool | None,
        select_labels: list[str] | None,
    ):

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass None (or omit the argument) to query all indexed series: client.ts().querylabels() or querylabels(filters=None).
  2. Provide at least one filter expression: querylabels(filters=['region=us']).
  3. Normalize optional input: filters = filters or None before calling.

Example fix

// before
client.ts().querylabels(filters=selected_filters)  # empty list
// after
client.ts().querylabels(filters=selected_filters or None)
Defensive patterns

Strategy: validation

Validate before calling

def coerce_filters(filters):
    # None means 'all series'; empty collection is rejected by the lib
    if filters is not None and len(list(filters)) == 0:
        return None
    return filters

# usage: client.ts().querylabels(filters=coerce_filters(f))

Type guard

def filters_ok(filters) -> bool:
    return filters is None or len(list(filters)) > 0

Try / catch

try:
    client.ts().querylabels(filters=f)
except Exception as e:
    if 'empty collection' in str(e):
        client.ts().querylabels()  # query all
    else:
        raise

Prevention

When it happens

Trigger: client.ts().querylabels(filters=[]) or querylabels(filters=exprs) where exprs is an empty list. Also querylabels('region', filters=[]) for the VALUES form.

Common situations: Forwarding an optional filter list that resolved to empty; migrating from TS.QUERYINDEX which accepts different semantics; assuming [] means 'all'.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/791f15a0030738fc.json. Report an issue: GitHub.