redis/redis-py · error · RedisError
Invalid slot state
Error message
Invalid slot state: {state} What it means
Raised by RedisCluster.cluster_setslot() when the `state` argument is not one of the accepted values. The method only accepts 'IMPORTING', 'NODE', 'MIGRATING' (which it forwards to CLUSTER SETSLOT) and treats 'STABLE' specially by redirecting to cluster_setslot_stable(). Any other value is rejected client-side before any command is sent to Redis. This is an input-validation guard, not a server error.
Solutions
- Use one of 'IMPORTING', 'NODE', or 'MIGRATING' as the state argument.
- If you meant to clear a slot's migration state, call cluster_setslot_stable(slot_id) instead of cluster_setslot(..., state='STABLE').
- Check the spelling and case of the state string against the accepted set.
Example fix
# before client.cluster_setslot(node, node_id, slot_id, state='MIGRATE') # after client.cluster_setslot(node, node_id, slot_id, state='MIGRATING') # or for clearing state: client.cluster_setslot_stable(slot_id)
Defensive patterns
Strategy: validation
Validate before calling
VALID_SLOT_STATES = {'IMPORTING', 'NODE', 'MIGRATING'}
def set_slot(node, node_id, slot_id, state):
s = state.upper()
if s == 'STABLE':
client.cluster_setslot_stable(slot_id)
elif s in VALID_SLOT_STATES:
client.cluster_setslot(node, node_id, slot_id, state)
else:
raise ValueError(f'state must be one of {VALID_SLOT_STATES | {"STABLE"}}, got {state!r}') Type guard
def is_valid_slot_state(state: str) -> bool:
return isinstance(state, str) and state.upper() in {'IMPORTING', 'NODE', 'MIGRATING', 'STABLE'} Try / catch
from redis.exceptions import RedisError
try:
client.cluster_setslot(node, node_id, slot_id, state)
except RedisError as e:
if 'Invalid slot state' in str(e):
# fix state and retry or surface config error
...
raise Prevention
- Centralize slot-migration logic behind a helper that validates the state enum.
- Treat 'STABLE' as a separate code path that calls cluster_setslot_stable().
- Add a unit test covering each accepted state plus one rejected value.
When it happens
Trigger: Calling client.cluster_setslot(target_node, node_id, slot_id, state) with a `state` string outside {'IMPORTING','NODE','MIGRATING','STABLE'} (case-insensitive). Examples: state='MIGRATE' (typo), state='MOVING', state='IMPORT', state='', or passing an int/None as state (AttributeError on .upper() aside, the branch falls through to the else).
Common situations: Migrating hash slots between cluster nodes manually; misspelling a slot state constant; using a value valid in redis-cli but not exposed by this client wrapper; copy-pasting from Redis docs that list states the client intentionally funnels through a separate method.
Related errors
- ACL LOG count must be an integer
- Cannot set 'nopass' and supply 'passwords' or…
- Category " " must be prefixed with "+" or
- CLIENT KILL type must be one of
- Command " " must be prefixed with "+" or
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/bc47f76a390fedd6.
Report an issue: GitHub.
Appendix: source
Thrown at redis/commands/cluster.py:890
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
"""
return self.execute_command("CLUSTER SETSLOT", slot_id, "STABLE")
View on GitHub (pinned to 6a6b581b48)