redis/redis-py · error · DataError

block_min_count requires block_milliseconds to be set; the B

Error message

block_min_count requires block_milliseconds to be set; the BLOCK group is all-or-nothing.

What it means

Raised by _append_block (used by TS.READ) when block_min_count is set but block_milliseconds is None. The BLOCK group on the wire is all-or-nothing: blocking is opted into via block_milliseconds, and min_count defaults to 1 only when blocking is enabled. Supplying a threshold without enabling blocking is a usage error.

Source

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

        if count is not None:
            params.extend(["COUNT", count])

    @staticmethod
    def _append_block(
        params: list[EncodableT],
        block_milliseconds: int | None,
        block_min_count: int | None,
    ):
        """Append the BLOCK group to params.

        The BLOCK group is all-or-nothing: when blocking is requested
        (`block_milliseconds` is set), both `milliseconds` and `min_count` are
        always emitted, with `min_count` defaulting to 1. There is no standalone
        MIN_COUNT keyword in this command.
        """
        if block_milliseconds is None:
            if block_min_count is not None:
                raise DataError(
                    "block_min_count requires block_milliseconds to be set; the "
                    "BLOCK group is all-or-nothing."
                )
            return
        min_count = 1 if block_min_count is None else block_min_count
        params.extend(["BLOCK", block_milliseconds, min_count])

    @staticmethod
    def _append_max_count(params: list[EncodableT], max_count: int | None):
        """Append MAX_COUNT property to params."""
        if max_count is not None:
            params.extend(["MAX_COUNT", max_count])

    @staticmethod
    def _append_timestamp(params: list[EncodableT], timestamp: int | None):
        """Append TIMESTAMP property to params."""
        if timestamp is not None:
            params.extend(["TIMESTAMP", timestamp])

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Set block_milliseconds alongside block_min_count: client.ts().read('key', '$', block_milliseconds=5000, block_min_count=5).
  2. If you do not want blocking, remove block_min_count entirely.
  3. Also raise the client socket_timeout above block_milliseconds to avoid a premature TimeoutError.

Example fix

// before
client.ts().read('key', '$', block_min_count=5)
// after
client.ts().read('key', '$', block_milliseconds=5000, block_min_count=5)
Defensive patterns

Strategy: validation

Validate before calling

def normalize_block(block_ms, block_min_count):
    if block_min_count is not None and block_ms is None:
        raise ValueError("block_min_count requires block_milliseconds")
    return block_ms, block_min_count

# usage: client.ts().read('k', '$', *normalize_block(ms, mc))

Type guard

def block_args_valid(block_ms, block_min_count) -> bool:
    return block_ms is not None or block_min_count is None

Try / catch

try:
    client.ts().read('k', '$', block_min_count=mc)
except Exception as e:
    if 'block_min_count requires' in str(e):
        client.ts().read('k', '$', block_milliseconds=5000, block_min_count=mc)
    else:
        raise

Prevention

When it happens

Trigger: client.ts().read('key', '$', block_min_count=5) with block_milliseconds left as None.

Common situations: Configuring the unblock threshold but forgetting the block duration; passing a partial BLOCK config from a dict that omitted milliseconds; assuming min_count alone enables blocking.

Related errors


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