{"record":{"id":"59c7013634669214","repo":"redis/redis-py","slug":"at-least-a-command-with-a-key-is-needed-to-identif","errorCode":null,"errorMessage":"At least a command with a key is needed to identify a node","messagePattern":"At least a command with a key is needed to identify a node","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/asyncio/cluster.py","lineNumber":3129,"sourceCode":"            # Prior slots came only from zero-key scripts; retarget.\n            self._pipeline_slots.clear()\n        if slot_number is not None:\n            self._transaction_has_keyed_slot = True\n        return slot_number\n\n    def _get_client_and_connection_for_transaction(\n        self,\n    ) -> Tuple[ClusterNode, Connection]:\n        \"\"\"\n        Find a connection for a pipeline transaction.\n\n        For running an atomic transaction, watch keys ensure that contents have not been\n        altered as long as the watch commands for those keys were sent over the same\n        connection. So once we start watching a key, we fetch a connection to the\n        node that owns that slot and reuse it.\n        \"\"\"\n        if not self._pipeline_slots:\n            raise RedisClusterException(\n                \"At least a command with a key is needed to identify a node\"\n            )\n\n        node: ClusterNode = self._pipe.cluster_client.nodes_manager.get_node_from_slot(\n            list(self._pipeline_slots)[0], False\n        )\n        self._transaction_node = node\n\n        if not self._transaction_connection:\n            connection: Connection = self._transaction_node.acquire_connection()\n            self._transaction_connection = connection\n\n        return self._transaction_node, self._transaction_connection\n\n    def execute_command(self, *args: Union[KeyT, EncodableT], **kwargs: Any) -> \"Any\":\n        # Given the limitation of ClusterPipeline sync API, we have to run it in thread.\n        response = None\n        error = None","sourceCodeStart":3111,"sourceCodeEnd":3147,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/cluster.py#L3111-L3147","documentation":"Raised by TransactionStrategy._get_client_and_connection_for_transaction (redis/asyncio/cluster.py:3117) when self._pipeline_slots is empty. A cluster transaction must pin a single node, which requires at least one keyed command to resolve a slot. If only slot-agnostic commands (e.g. UNWATCH) were queued, there is no slot from which to derive the owning node, so the transaction cannot be executed. Queue at least one keyed command before execute().","triggerScenarios":"Building a ClusterPipeline and calling execute() / multi()/EXEC after only slot-agnostic commands (UNWATCH, or zero-key EVAL) with no keyed command ever added; calling watch()/unwatch() cycle without a keyed command in between.","commonSituations":"Using pipeline purely to send ADMIN/CONFIG-style commands inside a transaction; empty or aborted transactions that only contained UNWATCH.","solutions":["Ensure the pipeline contains at least one keyed command (GET/SET/etc.) before execute()","If you only need slot-agnostic commands, send them directly on the client rather than through a transactional pipeline","For zero-key scripts, either pair them with a keyed command or send them directly via client.eval()"],"exampleFix":"// before\npipe = client.pipeline(transaction=True)\nawait pipe.unwatch()\nawait pipe.execute()\n// after - send slot-agnostic commands directly\nawait client.unwatch()","handlingStrategy":"validation","validationCode":"if not pipe._command_queue:  # or track that a keyed command was added\n    raise ValueError('add at least one keyed command before execute()')","typeGuard":null,"tryCatchPattern":"try:\n    await pipe.execute()\nexcept RedisClusterException as e:\n    if 'At least a command with a key' in str(e):\n        # add a keyed command or skip the transaction\n        ...","preventionTips":["Always queue at least one keyed command in a cluster transaction","Send slot-agnostic commands (UNWATCH, CONFIG) directly on the client"],"tags":["cluster","pipeline","transaction","slot-routing"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}