redis/redis-py · error · DataError

EXCLUDEEMPTY is not allowed with GROUPBY

Error message

EXCLUDEEMPTY is not allowed with GROUPBY

What it means

Raised by the TS.MRANGE/TS.MREVRANGE parameter builder when both exclude_empty=True and groupby are set. EXCLUDEEMPTY filters out empty series before grouping, which conflicts with GROUPBY semantics (it would drop groups). The library rejects the combination up front.

Source

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

        reduce: str | None,
        select_labels: List[str] | None,
        align: int | str | None,
        latest: bool | None,
        bucket_timestamp: str | None,
        empty: bool | None,
        exclude_empty: bool | None,
    ):
        """Create TS.MRANGE and TS.MREVRANGE arguments."""
        if (
            groupby is not None
            and isinstance(aggregation_type, list)
            and len(aggregation_type) > 1
        ):
            raise DataError(
                "GROUPBY is not allowed when multiple aggregators are specified"
            )
        if exclude_empty and groupby is not None:
            raise DataError("EXCLUDEEMPTY is not allowed with GROUPBY")
        params: list[EncodableT] = [from_time, to_time]
        self._append_latest(params, latest)
        self._append_filer_by_ts(params, filter_by_ts)
        self._append_filer_by_value(params, filter_by_min_value, filter_by_max_value)
        self._append_with_labels(params, with_labels, select_labels)
        self._append_count(params, count)
        self._append_align(params, align)
        self._append_aggregation(params, aggregation_type, bucket_size_msec)
        self._append_bucket_timestamp(params, bucket_timestamp)
        self._append_empty(params, empty)
        self._append_exclude_empty(params, exclude_empty)
        params.extend(["FILTER"])
        params += filters
        self._append_groupby_reduce(params, groupby, reduce)
        return params

    @overload
    def mrange(

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Drop exclude_empty when using GROUPBY: set exclude_empty=False (the default).
  2. If you must exclude empty series, pre-filter the series via a TS.QUERYINDEX/QUERYLABELS lookup instead of EXCLUDEEMPTY.
  3. Keep GROUPBY and accept that empty groups may appear.

Example fix

// before
client.ts().mrange('-', '+', filters=f, groupby='region', reduce='sum', exclude_empty=True)
// after
client.ts().mrange('-', '+', filters=f, groupby='region', reduce='sum', exclude_empty=False)
Defensive patterns

Strategy: validation

Validate before calling

def normalize_mrange_opts(groupby, exclude_empty):
    if groupby is not None and exclude_empty:
        return groupby, False  # drop EXCLUDEEMPTY when grouping
    return groupby, exclude_empty

Type guard

def groupby_exclude_empty_compatible(groupby, exclude_empty) -> bool:
    return not (exclude_empty and groupby is not None)

Try / catch

try:
    client.ts().mrange('-', '+', filters=f, groupby=g, reduce=r, exclude_empty=ee)
except Exception as e:
    if 'EXCLUDEEMPTY' in str(e):
        client.ts().mrange('-', '+', filters=f, groupby=g, reduce=r, exclude_empty=False)
    else:
        raise

Prevention

When it happens

Trigger: client.ts().mrange('-', '+', filters=f, groupby='region', reduce='sum', exclude_empty=True).

Common situations: Adding EXCLUDEEMPTY to reduce noise while also grouping; copying options from a non-grouped query into a grouped one; enabling both via a shared config dict.

Related errors


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