redis/redis-py · error · RedisClusterException
At least a command with a key is needed to identify a node
Error message
At least a command with a key is needed to identify a node
What it means
Raised by TransactionStrategy._get_client_and_connection_for_transaction (redis/cluster.py:4648) when _pipeline_slots is empty. A cluster transaction must pin to a single slot to choose its connection; if no keyed command has established a slot yet (only slot-agnostic commands queued), the library cannot pick a node and aborts.
Solutions
- Ensure at least one keyed command (GET/SET/etc.) is queued before execute() so a slot is fixed.
- If you only need keyless commands, use a non-transactional pipeline or execute directly on a node.
- For WATCH, watch a key first so the slot is established before MULTI.
Example fix
// before
with rc.pipeline(transaction=True) as pipe:
pipe.ping()
pipe.execute()
// after
with rc.pipeline(transaction=True) as pipe:
pipe.set('anchor:{tag}', '1')
pipe.ping()
pipe.execute() Defensive patterns
Strategy: validation
Validate before calling
with rc.pipeline(transaction=True) as pipe:
if not any(cmd_has_key(c) for c in planned_commands):
pipe.set('anchor:{tag}', '1') # ensure a slot is fixed
for c in planned_commands:
pipe.execute_command(*c)
pipe.execute() Type guard
def has_keyed_command(commands) -> bool:
keyless = {'PING','INFO','DBSIZE','FLUSHALL','FLUSHDB'}
return any(c[0].upper() not in keyless for c in commands) Try / catch
from redis.exceptions import RedisClusterException
try:
pipe.execute()
except RedisClusterException as e:
if 'At least a command with a key' in str(e):
# queue a keyed command and retry, or use a non-transactional pipeline
... Prevention
- Always queue at least one keyed command in a transactional pipeline.
- Use non-transactional pipelines for purely keyless/admin work.
When it happens
Trigger: Opening a transactional pipeline and calling execute() having queued only keyless/slot-agnostic commands (e.g. only PING, INFO, or zero-key EVAL), or calling watch()/immediate commands before any key has fixed a slot.
Common situations: Building a transaction dynamically and forgetting to include a keyed command; executing an empty-ish transaction for health checks; race where the first keyed command was skipped due to a condition.
Related errors
- Cannot identify slot number for command
- All keys involved in a cluster transaction must map to the…
- Cannot watch or send commands on different slots
- Cannot watch or send commands on different slots
- Too many targets for command
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/9c9d1eb1497eea1d.
Report an issue: GitHub.
Appendix: source
Thrown at redis/cluster.py:4648
and not self._transaction_has_keyed_slot
):
# Prior slots came only from zero-key scripts; retarget.
self._pipeline_slots.clear()
if slot_number is not None:
self._transaction_has_keyed_slot = True
return slot_number
def _get_client_and_connection_for_transaction(self) -> Tuple[Redis, Connection]:
"""
Find a connection for a pipeline transaction.
For running an atomic transaction, watch keys ensure that contents have not been
altered as long as the watch commands for those keys were sent over the same
connection. So once we start watching a key, we fetch a connection to the
node that owns that slot and reuse it.
"""
if not self._pipeline_slots:
raise RedisClusterException(
"At least a command with a key is needed to identify a node"
)
node: ClusterNode = self._nodes_manager.get_node_from_slot(
list(self._pipeline_slots)[0], False
)
redis_node: Redis = self._pipe.get_redis_connection(node)
if self._transaction_connection:
if not redis_node.connection_pool.owns_connection(
self._transaction_connection
):
previous_node = self._nodes_manager.find_connection_owner(
self._transaction_connection
)
previous_node.connection_pool.release(self._transaction_connection)
self._transaction_connection = None
if not self._transaction_connection:View on GitHub (pinned to 6a6b581b48)