redis/redis-py · error · RedisError

For "stable" state please use cluster_setslot_stable

Error message

For "stable" state please use cluster_setslot_stable

What it means

Raised by ClusterManagementCommands.cluster_setslot() in redis/commands/cluster.py:888 when the state argument is 'STABLE'. The library splits CLUSTER SETSLOT into two methods because the STABLE state does not require a node_id argument, so it routes you to cluster_setslot_stable(slot_id) which has the correct signature.

Solutions

  1. For the STABLE state call rc.cluster_setslot_stable(slot_id) instead.
  2. Only pass state in {'IMPORTING','NODE','MIGRATING'} to cluster_setslot().
  3. Refactor generic slot code to branch: if state.upper()=='STABLE': use cluster_setslot_stable().

Example fix

// before
rc.cluster_setslot(node, node_id, slot_id, 'STABLE')
// after
rc.cluster_setslot_stable(slot_id)
Defensive patterns

Strategy: validation

Validate before calling

if state.strip().upper() == 'STABLE':
    rc.cluster_setslot_stable(slot_id)
else:
    rc.cluster_setslot(target_node, node_id, slot_id, state)

Type guard

def is_stable_state(state) -> bool:
    return isinstance(state, str) and state.strip().upper() == 'STABLE'

Try / catch

from redis.exceptions import RedisError
try:
    rc.cluster_setslot(node, node_id, slot_id, state)
except RedisError as e:
    if 'stable" state' in str(e):
        rc.cluster_setslot_stable(slot_id)

Prevention

When it happens

Trigger: Calling rc.cluster_setslot(target_node, node_id, slot_id, state='STABLE'), or passing state='stable' (case-insensitive match).

Common situations: Generic slot-management code that handles all states in one call; copy-paste of the IMPORTING/MIGRATING/NODE call with state swapped to STABLE without dropping node_id.

Related errors


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

Appendix: source

Thrown at redis/commands/cluster.py:888

    ) -> Awaitable[bool]: ...

    def cluster_setslot(
        self, target_node: "TargetNodesT", node_id: str, slot_id: int, state: str
    ) -> bool | Awaitable[bool]:
        """
        Bind an hash slot to a specific node

        :target_node: 'ClusterNode'
            The node to execute the command on

        For more information see https://redis.io/commands/cluster-setslot
        """
        if state.upper() in ("IMPORTING", "NODE", "MIGRATING"):
            return self.execute_command(
                "CLUSTER SETSLOT", slot_id, state, node_id, target_nodes=target_node
            )
        elif state.upper() == "STABLE":
            raise RedisError('For "stable" state please use cluster_setslot_stable')
        else:
            raise RedisError(f"Invalid slot state: {state}")

    @overload
    def cluster_setslot_stable(self: SyncClientProtocol, slot_id: int) -> bool: ...

    @overload
    def cluster_setslot_stable(
        self: AsyncClientProtocol, slot_id: int
    ) -> Awaitable[bool]: ...

    def cluster_setslot_stable(self, slot_id: int) -> bool | Awaitable[bool]:
        """
        Clears migrating / importing state from the slot.
        It determines by it self what node the slot is in and sends it there.

        For more information see https://redis.io/commands/cluster-setslot
        """

View on GitHub (pinned to 6a6b581b48)