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
- Run the FT.AGGREGATE query on the same RedisCluster instance before issuing any FT.CURSOR command so _aggregate_nodes is populated.
- If you need to page results across restarts, re-run FT.AGGREGATE with WITHCURSOR to get a fresh cursor on the current client.
- 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
- Always run FT.AGGREGATE with WITHCURSOR on the same client instance before paging with FT.CURSOR.
- Do not persist cursor IDs across client restarts or different instances.
- Keep the aggregate+cursor lifecycle within a single RedisCluster object.
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
- Bad query
- Bad query type
- Cannot execute FT.CURSOR commands without FT.AGGREGATE
- Cannot set 'sortable' or 'no_index' in Vector fields.
- Cannot use FIELDNAME alias with no field
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)