redis/redis-py · error · RedisClusterException

Too many targets for command

Error message

Too many targets for command {cmd.args}

What it means

Raised in ClusterPipeline._execute() when a queued command resolves to more than one target node. The transactional/non-transactional pipeline strategies send each queued command to exactly one node (cluster pipelines are slot-pinned per command), so a command that would fan out to multiple nodes is rejected because the pipeline cannot represent a multi-node result for one entry.

Solutions

  1. Pin each queued command to a single node: pass target_nodes=<one ClusterNode> or a single node-flag that resolves to one node.
  2. For cross-key bulk writes use mset_nonatomic() which splits per slot, or run separate pipelines per slot.
  3. For commands you genuinely want on every node, execute them directly via execute_command(target_nodes=rc.ALL_NODES) outside the pipeline.
  4. Ensure multi-key pipeline commands share a hash tag so they resolve to one slot.

Example fix

// before
pipe = rc.pipeline()
pipe.execute_command('CONFIG', 'REWRITE', target_nodes=rc.ALL_NODES)  # >1 node
await pipe.execute()

// after
await rc.execute_command('CONFIG', 'REWRITE', target_nodes=rc.ALL_NODES)
# or pin the pipeline entry:
pipe = rc.pipeline()
pipe.execute_command('CONFIG', 'REWRITE', target_nodes=rc.get_primaries()[0])
await pipe.execute()
Defensive patterns

Strategy: type-guard

Validate before calling

def single_node_targets(rc, target_nodes):
    nodes = rc._parse_target_nodes(target_nodes)
    if len(nodes) > 1:
        raise ValueError('pipeline commands require exactly one target node')
    return nodes

pipe = rc.pipeline()
pipe.execute_command('CONFIG', 'REWRITE',
                    target_nodes=rc.get_primaries()[0])

Type guard

def is_single_target(rc, t) -> bool:
    try:
        return len(rc._parse_target_nodes(t)) == 1
    except TypeError:
        return False

Try / catch

from redis.exceptions import RedisClusterException
try:
    await pipe.execute()
except RedisClusterException as e:
    if 'Too many targets' in str(e):
        # move the offending command out of the pipeline and run it directly
        await rc.execute_command(*cmd.args, target_nodes=rc.ALL_NODES)

Prevention

When it happens

Trigger: Queuing a command with an ALL_NODES/PRIMARIES policy or passing target_nodes=[nodeA, nodeB] to a single pipeline entry. Also a multi-key command whose keys do not share a slot when the pipeline is in transaction mode (single-slot constraint).

Common situations: Treating a cluster pipeline like the standalone pipeline and expecting fan-out; pipelining cross-slot keys; passing a list of nodes as target_nodes for one queued command.

Related errors


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

Appendix: source

Thrown at redis/asyncio/cluster.py:2945

                                request_policy=client._command_flags_mapping[
                                    command_flag
                                ]
                            )
                        else:
                            command_policies = CommandPolicies()

                target_nodes = await client._determine_nodes(
                    *cmd.args,
                    request_policy=command_policies.request_policy,
                    node_flag=passed_targets,
                )
                if not target_nodes:
                    raise RedisClusterException(
                        f"No targets were found to execute {cmd.args} command on"
                    )
            cmd.command_policies = command_policies
            if len(target_nodes) > 1:
                raise RedisClusterException(f"Too many targets for command {cmd.args}")
            node = target_nodes[0]
            if node.name not in nodes:
                nodes[node.name] = (node, [])
            nodes[node.name][1].append(cmd)

        # Start timing for observability
        start_time = time.monotonic()

        errors = await asyncio.gather(
            *(
                asyncio.create_task(node[0].execute_pipeline(node[1]))
                for node in nodes.values()
            )
        )

        # Record operation duration for each node
        for node_name, (node, commands) in nodes.items():
            # Find the first error in this node's commands, if any

View on GitHub (pinned to 6a6b581b48)