redis/redis-py · error · RedisClusterException

Cannot execute FT.CURSOR commands without FT.AGGREGATE

Error message

Cannot execute FT.CURSOR commands without FT.AGGREGATE

What it means

Raised as a RedisClusterException by get_special_nodes() when FT.CURSOR READ or FT.CURSOR DEL is attempted before FT.AGGREGATE has been executed in the current client session. The client stores the aggregate target nodes in self._aggregate_nodes only when it processes an FT.AGGREGATE command (cluster.py:1369-1370), and FT.CURSOR must route to those same nodes. Without a prior FT.AGGREGATE, there is no valid target node set for the cursor.

Solutions

  1. Run the FT.AGGREGATE query on the same RedisCluster instance before issuing any FT.CURSOR command so _aggregate_nodes is populated.
  2. If you need to page results across restarts, re-run FT.AGGREGATE with WITHCURSOR to get a fresh cursor on the current client.
  3. Do not share cursor IDs between different client instances; each instance must own its own aggregate+cursor lifecycle.

Example fix

// before
r.execute_command('FT.CURSOR READ', 'myidx', cursor_id)

// after
r.execute_command('FT.AGGREGATE', 'myidx', '*', 'WITHCURSOR', 'COUNT', 10)
r.execute_command('FT.CURSOR READ', 'myidx', cursor_id)
Defensive patterns

Strategy: validation

Validate before calling

# Ensure FT.AGGREGATE runs before FT.CURSOR on the same instance
if not getattr(client, '_aggregate_nodes', None):
    raise RuntimeError('Run FT.AGGREGATE with WITHCURSOR before FT.CURSOR')

Type guard

def has_aggregate_nodes(client) -> bool:
    return getattr(client, '_aggregate_nodes', None) is not None

Try / catch

from redis.exceptions import RedisClusterException
try:
    client.execute_command('FT.CURSOR READ', idx, cursor)
except RedisClusterException as e:
    if 'FT.AGGREGATE' in str(e):
        client.execute_command('FT.AGGREGATE', idx, '*', 'WITHCURSOR')

Prevention

When it happens

Trigger: Calling r.execute_command('FT.CURSOR READ', index, cursor_id, ...) or the ft().cursor_read() helper on a RedisCluster instance without first running an FT.AGGREGATE on that same instance, so self._aggregate_nodes is still None (initialized at cluster.py:983).

Common situations: Developer calls FT.CURSOR to read the next page of results from a cursor obtained in a previous session or from a different client instance, or the FT.AGGREGATE was run on a different RedisCluster object so _aggregate_nodes was never populated.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/5e3afe4b77bb4806. Report an issue: GitHub.

Appendix: source

Thrown at redis/cluster.py:1123

        keys = self._get_command_keys(*args)
        commands = []

        for key in keys:
            commands.append(
                {
                    "args": (args[0], key),
                    "kwargs": kwargs,
                }
            )

        return commands

    def get_special_nodes(self) -> Optional[list["ClusterNode"]]:
        """
        Returns a list of nodes for commands with a special policy.
        """
        if not self._aggregate_nodes:
            raise RedisClusterException(
                "Cannot execute FT.CURSOR commands without FT.AGGREGATE"
            )

        return self._aggregate_nodes

    def get_random_primary_node(self) -> "ClusterNode":
        """
        Returns a random primary node
        """
        return random.choice(self.get_primaries())

    def _evaluate_all_succeeded(self, res):
        """
        Evaluate the result of a command with ResponsePolicy.ALL_SUCCEEDED
        """
        first_successful_response = None

        if isinstance(res, dict):

View on GitHub (pinned to 6a6b581b48)