The Redis MOVED error indicates that a hash slot has been moved to a different node in a Redis Cluster. This is a client-side redirection issue that occurs when a client connects to a cluster node that no longer owns the requested slot. Fixing requires using cluster-aware Redis clients or enabling cluster mode in your client library.
The MOVED error is a fundamental part of Redis Cluster's slot migration and routing mechanism. When a Redis client sends a command to a cluster node, Redis first calculates which hash slot the key belongs to based on the CRC16 hash of the key. If that node owns the slot, the command executes normally. However, if the node does not own that slot—either because the slot was moved to a different node during cluster rebalancing, or because the client connected to the wrong node initially—Redis returns a MOVED redirection. The error format is: `(error) MOVED {slot_id} {node_host}:{node_port}` where `{slot_id}` is the hash slot number (0-16383) and `{node_host}:{node_port}` is the address of the node that currently owns that slot. This is NOT an error in the traditional sense—it is a redirect instruction. Properly implemented cluster-aware Redis clients automatically update their internal slot-to-node mapping when they receive a MOVED response and retry the command against the correct node. If you see this error in your application logs, it means either: 1) your client is not cluster-aware, 2) your client library is treating the cluster as a single Redis instance, or 3) the client's internal slot mapping is stale and needs to be refreshed.
The MOVED error occurs because your client does not know which slot is on which node. Most popular Redis clients have cluster-aware implementations:
Node.js (ioredis - recommended):
const Redis = require("ioredis");
const cluster = new Redis.Cluster([
{ host: "node1.example.com", port: 6379 },
{ host: "node2.example.com", port: 6379 },
{ host: "node3.example.com", port: 6379 }
]);
await cluster.set("mykey", "value");Python (redis-py):
from redis.cluster import RedisCluster
rc = RedisCluster(
startup_nodes=[{"host": "node1.example.com", "port": 6379}],
decode_responses=True
)
rc.set("mykey", "value")Java (Jedis):
HostAndPort hp = new HostAndPort("node1.example.com", 6379);
JedisCluster jedisCluster = new JedisCluster(hp);
jedisCluster.set("mykey", "value");Go (go-redis/redis):
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{
"node1.example.com:6379",
"node2.example.com:6379",
"node3.example.com:6379",
},
})
rdb.Set(ctx, "mykey", "value", 0)C# (StackExchange.Redis):
var options = ConfigurationOptions.Parse("node1.example.com:6379,node2.example.com:6379,node3.example.com:6379");
var connection = ConnectionMultiplexer.Connect(options);
var db = connection.GetDatabase();
db.StringSet("mykey", "value");Ensure your library is cluster-enabled and passes all cluster nodes during initialization, not just a single node.
If you are using redis-cli with a Redis Cluster, you must add the -c flag to enable cluster mode. Without it, redis-cli treats the cluster as a single node and will fail on MOVED errors.
# WRONG - will produce MOVED errors:
redis-cli -h node1.example.com -p 6379
# CORRECT - enables cluster support:
redis-cli -c -h node1.example.com -p 6379With -c enabled, redis-cli automatically follows MOVED redirects and updates its internal slot map.
For redis-benchmark:
# Enable cluster mode:
redis-benchmark -c -h node1.example.com -p 6379 -n 10000Cluster-aware clients use the CLUSTER SLOTS or CLUSTER NODES command to build a complete map of which slots are on which nodes. This map must be initialized from a working node.
# Verify the cluster is healthy and all nodes are reachable:
redis-cli -c cluster nodes
# Output should show all master nodes with their slot ranges:
# 6379d1f37a77f68b7d5a2eb22cb8adb1d25cae2f node1.example.com:6379@16379 myself,master - 0 0 1 connected 0-5460
# 2a2ecc9e9f97ca9adb0cb98c9d72a9e98e6ac62f node2.example.com:6379@16379 master - 0 1639486400000 2 connected 5461-10922
# 8de1dd82f99c1cffd6ae842a2e9d6b31d8f91df9 node3.example.com:6379@16379 master - 0 1639486400000 3 connected 10923-16383If any nodes are unreachable or the cluster is not "OK", fix those issues first. Then initialize your client with at least one working node address—the client will automatically discover the rest.
When a cluster rebalance occurs (due to adding/removing nodes or explicit slot migration), clients may have outdated slot-to-node mappings. Some clients support explicit refresh:
redis-py:
# Reinitialize the cluster on every MOVED error:
rc = RedisCluster(
startup_nodes=[{"host": "node1.example.com", "port": 6379}],
reinitialize_steps=1 # Forces full cluster refresh on MOVED
)
# Or manually refresh:
rc.cluster_slots()ioredis:
ioredis automatically refreshes the slot mapping on MOVED errors. If you need to force a refresh:
cluster.flushAll()
.then(() => cluster.reset(false)) // Reset without closing connection
.then(() => cluster.info('replication'))Go (go-redis):
go-redis automatically handles MOVED redirects and updates its internal mapping. No manual action needed.
Most modern cluster clients handle this automatically when they receive MOVED responses. If MOVED errors persist, it usually indicates a client configuration issue, not a cluster problem.
If MOVED errors occur frequently during normal operation (not just during rebalancing), the cluster itself may have an issue:
# Check cluster status:
redis-cli -c cluster info
# Output should show "cluster_state:ok"
# If it shows "cluster_state:fail", investigate which nodes are down:
redis-cli -c cluster nodes
# Look for nodes marked as "fail" or "disconnected"
# Reconnect failed nodes or remove them from the clusterIf the cluster shows "fail" state:
# Check the error log on affected nodes:
tail -50 /var/log/redis/redis-server.log
# If a node is permanently down, remove it:
redis-cli -c cluster forget <node-id>
# If a node is hung, restart it:
sudo systemctl restart redis-server
# Wait for the cluster to stabilize (30+ seconds):
watch -n1 "redis-cli -c cluster info | grep cluster_state"A healthy cluster should show "cluster_state:ok" and have all slots assigned to reachable nodes.
Redis Cluster uses the CRC16 hash function to calculate which of 16,384 slots a key belongs to: slot = CRC16(key) mod 16384. The MOVED redirection instructs the client to route that slot's operations to a different node. This is a permanent redirect—once you receive MOVED, you should update your slot mapping to route all future commands for that slot to the new node.
Different from MOVED is the ASK redirection, which occurs during slot migration and is temporary. ASK means "try the next command on this node only"; MOVED means "always route this slot to the specified node from now on".
For testing cluster connectivity without a full client library, you can manually follow MOVED redirects:
redis-cli -h node1.example.com -p 6379 <<EOF
get mykey
EOF
# If MOVED is returned, reconnect to the new node and retryIn containerized or cloud environments, ensure all cluster nodes are DNS-resolvable and network-accessible from your application. If behind NAT or a reverse proxy, the "node address" in MOVED responses must match actual connectivity. Some Redis cluster setups require configuration of "cluster-announce-ip" and "cluster-announce-port" to match external addresses.
For monitoring, track MOVED error rates. Occasional MOVEDs during rebalancing are normal; frequent MOVEDs in stable clusters indicate client configuration issues or network problems. Use cluster-aware monitoring tools like redis-exporter or client library metrics to detect these patterns.
ERR Unbalanced XREAD list of streams
How to fix "ERR Unbalanced XREAD list" in Redis
ERR syntax error
How to fix "ERR syntax error" in Redis
ConnectionError: Error while reading from socket
ConnectionError: Error while reading from socket in redis-py
ERR unknown command
How to fix ERR unknown command in Redis
Command timed out
How to fix 'Command timed out' in ioredis