Why You're Seeing This ErrorMoving from a standalone Redis instance to a cluster often feels like a smooth transition—until you try to run a Lua script. Suddenly, your EVAL command crashes. You get a frustrating error: ERR Script attempted to access a non-local key in a cluster node. This happens because a cluster is a distributed system where data is scattered across multiple nodes. Unlike a single instance, a cluster node only 'owns' a specific slice of your data.
The Root Cause: Data Sharding and Hash SlotsRedis Cluster divides the keyspace into exactly 16,384 hash slots. Each node handles a specific range of these slots. For example, Node A might handle slots 0 to 5,460, while Node B takes 5,461 to 10,922. When you run a Lua script, Redis requires that every key the script touches must live in the same hash slot.
The error triggers in these scenarios:
- Your script tries to read
key_afrom Node 1 andkey_bfrom Node 2.- You hardcoded a key name inside the script that doesn't belong to the node receiving the command.- You passed keys as arguments that hash to different slots.Redis enforces this rule strictly to maintain atomicity. If a script could touch keys across different nodes, Redis would have to implement expensive cross-node locking, which would destroy performance.
How to Fix the Error### Solution 1: Use Hash Tags for Key GroupingThe most reliable fix is using Hash Tags. By wrapping part of a key name in curly braces {}, you tell Redis to only hash the text inside those braces. This forces different keys to land in the same slot on the same node.
Example: Instead of these keys, which could end up on three different nodes:
user:123:profile
user:123:settings
user:123:stats
Use hash tags to group them together:
{user:123}:profile
{user:123}:settings
{user:123}:stats
Now, Redis hashes only the string user:123. This ensures all three keys live in the same slot, making them accessible to a single Lua script.
Solution 2: Always Pass Keys via the KEYS ArrayRedis Cluster needs to know which keys a script will touch before it actually runs. This allows the client to route the request to the correct node. If you hardcode a key inside your Lua code, the cluster cannot validate the location beforehand.
The Wrong Way (Hardcoded):
-- This will likely fail in a cluster environment
local val = redis.call('GET', 'global_settings')
return val
The Right Way (Passed as Argument):
-- Pass 'global_settings' as KEYS[1] from your application code
local val = redis.call('GET', KEYS[1])
return val
When you use the KEYS array, cluster-aware clients like ioredis, lettuce, or redis-py calculate the slot first. They then send the script to the specific node that holds that key.
Solution 3: Move Logic to the Application LayerSometimes you simply cannot co-locate keys. If you need to compare data between two users who live in different slots, a single Lua script won't work. In this case, you must handle the logic in your application code:
- Fetch
user:123:datafrom Redis.- Fetchuser:456:datafrom Redis.- Perform your comparison or logic in Node.js, Go, or Python.- Write the results back to Redis if needed.## Verification StepsCheck if your keys share the same slot using thecluster keyslotcommand inredis-cli:
# Check slot for the profile key
redis-cli -c CLUSTER KEYSLOT {user:123}:profile
# Output: 1234
# Check slot for the settings key
redis-cli -c CLUSTER KEYSLOT {user:123}:settings
# Output: 1234
If the output numbers match, your script will run without errors.

