This error occurs when a Lua script in Redis Cluster accesses keys without explicitly declaring them in the KEYS array. Redis Cluster requires all key names to be passed as input arguments for proper shard routing.
This error is specific to Redis Cluster environments and occurs when a Lua script attempts to access Redis keys without properly declaring them through the KEYS array parameter. In Redis Cluster, data is distributed across multiple nodes based on key hash slots. Before executing a Lua script, Redis must determine which node should run the script by analyzing which keys it will access. When you use dynamic key names (Lua variables, string concatenation, or keys stored in data structures) instead of the KEYS array, Redis cannot perform this pre-execution analysis. The cluster doesn't know which node contains the required data, making it impossible to route the script correctly. This restriction is particularly strict in managed Redis services like Alibaba Cloud (Aliyun) Redis and AWS ElastiCache, where additional validation is enforced to prevent cross-slot errors and ensure cluster stability.
Review your Lua script to find all redis.call() or redis.pcall() operations that access keys. Look for patterns like this:
-- ❌ WRONG: Using Lua variable for key name
local key = "user:123"
redis.call('GET', key)
-- ❌ WRONG: String concatenation
redis.call('SET', 'user:' .. userId, value)
-- ❌ WRONG: Keys in expressions
local keys = {'key1', 'key2'}
redis.call('GET', keys[1])The problem is that Redis Cluster cannot analyze these dynamic references before execution.
Modify your Lua script to reference the KEYS array directly in all redis.call() operations:
-- ✅ CORRECT: Using KEYS array
redis.call('GET', KEYS[1])
redis.call('SET', KEYS[2], ARGV[1])
-- ✅ CORRECT: Multiple keys from KEYS array
for i = 1, #KEYS do
redis.call('DEL', KEYS[i])
endImportant rules:
- All key names must be accessed via KEYS[1], KEYS[2], etc.
- Use ARGV array for non-key arguments (values, flags, options)
- Never use Lua variables where KEYS should be used
When calling EVAL or EVALSHA, you must specify the number of keys and pass them as arguments:
# ❌ WRONG: No keys declared
EVAL "return redis.call('GET', 'user:123')" 0
# ✅ CORRECT: Declare 1 key
EVAL "return redis.call('GET', KEYS[1])" 1 user:123
# ✅ CORRECT: Multiple keys and arguments
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 user:123 "John"In client libraries:
// Node.js ioredis
await redis.eval(
"return redis.call('GET', KEYS[1])",
1, // number of keys
'user:123' // key name
);
// Python redis-py
redis.eval(
"return redis.call('SET', KEYS[1], ARGV[1])",
1, # numkeys
'user:123', # key
'value' # argument
)If your script accesses multiple keys, they must hash to the same slot. Use hash tags to guarantee this:
-- ✅ CORRECT: Hash tags ensure same slot
redis.call('GET', KEYS[1]) -- {user:123}:profile
redis.call('GET', KEYS[2]) -- {user:123}:settingsWhen calling the script:
await redis.eval(
script,
2,
'{user:123}:profile', // Same hash tag {user:123}
'{user:123}:settings' // Same hash tag {user:123}
);Redis hashes only the content inside curly braces, so both keys will be on the same cluster node.
Verify your script works in cluster mode:
# Test with Redis CLI
redis-cli --cluster call <host>:6379 EVAL "return redis.call('GET', KEYS[1])" 1 testkey
# Check cluster slot assignment
redis-cli CLUSTER KEYSLOT testkeyIn your application, add error handling:
try {
const result = await redis.eval(script, numKeys, ...keys, ...args);
console.log('Script executed successfully:', result);
} catch (error) {
if (error.message.includes('bad lua script')) {
console.error('Script still uses dynamic keys - review KEYS array usage');
} else if (error.message.includes('CROSSSLOT')) {
console.error('Keys are in different slots - use hash tags');
}
throw error;
}Alibaba Cloud (Aliyun) Redis Clusters:
Aliyun enforces stricter Lua script validation than standard Redis. Even scripts that might work on self-hosted clusters may fail. They provide a script_check_enable parameter you can set to 0 to disable validation, but this is not recommended as it can cause unpredictable behavior and data corruption.
KEYS vs ARGV semantics:
The KEYS array is special in Redis Cluster - it's used for slot calculation and routing. ARGV is for regular arguments (values, counters, flags). Mixing these up is a common mistake. If you're passing a key name, it belongs in KEYS, not ARGV.
Performance considerations:
Declaring keys explicitly has minimal overhead. Redis Cluster calculates hash slots using CRC16, which is extremely fast. The real benefit is preventing runtime errors and ensuring atomic operations stay on a single node.
Functions vs EVAL:
Redis 7.0+ introduced Redis Functions, which have similar KEYS requirements. When using FUNCTION LOAD, the same rules apply - use keys_function() to declare keys that will be passed in the KEYS array.
Cross-slot operations:
If you absolutely need to access keys across different slots, you cannot use Lua scripts atomically. Instead, use Redis Cluster's MULTI-EXEC with hash tags, or redesign your data model to co-locate related keys using consistent hash tags.
ERR fsync error
How to fix "ERR fsync error" in Redis
CLUSTERDOWN The cluster is down
How to fix 'CLUSTERDOWN The cluster is down' in Redis
ERR Job for redis-server.service failed because a timeout was exceeded
Job for redis-server.service failed because a timeout was exceeded
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