The Problem
Redis Lua scripts provide atomic execution but can fail in multiple ways: syntax errors during loading, runtime errors during execution, or timeout errors that block the server. Understanding which type of failure you're facing determines the resolution path.
Understanding the Error Types
Syntax Errors
Syntax errors occur when loading the script:
ERR Error compiling script (new function): user_script:1: ')' expected near 'then'Runtime Errors
Runtime errors occur during script execution:
ERR Error running script (call to f_1234567890abcdef): @user_script:3: ERR value is not an integer or out of rangeTimeout Errors
Scripts running too long get killed:
BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.Diagnosis Commands
Check Script Load
Test script syntax without execution:
redis-cli SCRIPT LOAD "local x = 1 return x"If it returns a SHA1 hash, syntax is valid. If it returns an error, the script has syntax issues.
Debug Mode
Enable debugging to see which line fails:
redis-cli SCRIPT DEBUG yesThen run your script - Redis will provide detailed line-by-line error information.
Check Running Scripts
redis-cli CLIENT LIST | grep -i luaThis shows any clients currently executing Lua scripts.
Common Script Errors and Fixes
Error 1: Type Mismatch in Operations
-- FAILS: KEYS[1] might not exist or contain wrong type
local val = redis.call('GET', KEYS[1])
return val + 1Fix: Add type checking:
local val = redis.call('GET', KEYS[1])
if val and tonumber(val) then
return tonumber(val) + 1
else
return nil
endError 2: Accessing Non-Existent Keys
-- FAILS when key doesn't exist
local list = redis.call('LRANGE', KEYS[1], 0, -1)
return list[1]Fix: Check key existence:
if redis.call('EXISTS', KEYS[1]) == 1 then
local list = redis.call('LRANGE', KEYS[1], 0, -1)
return list[1]
end
return nilError 3: Wrong Number of Arguments
ERR wrong number of arguments for 'eval' commandFix: Ensure you pass the correct format:
# Format: EVAL script numkeys key [key ...] arg [arg ...]
redis-cli EVAL "return KEYS[1]" 1 mykeyThe number 1 indicates one key follows.
Error 4: Global Variable Usage
-- FAILS: Redis prohibits global variables
counter = 0
for i = 1, 10 do
counter = counter + 1
end
return counterFix: Use local variables:
local counter = 0
for i = 1, 10 do
counter = counter + 1
end
return counterHandling Script Timeouts
Scripts blocking Redis too long trigger the busy error. First, check the timeout setting:
redis-cli CONFIG GET lua-time-limitDefault is 5000 milliseconds.
Resolution for Running Stuck Script
If a script is currently stuck:
# Stop the script (only works if script hasn't written anything)
redis-cli SCRIPT KILLIf SCRIPT KILL fails because the script already wrote data:
# Last resort - this loses all unsaved data
redis-cli SHUTDOWN NOSAVEPrevention: Optimize Long-Running Scripts
Break complex operations into smaller scripts:
-- Instead of processing 10000 items in one script
-- Process 100 items per script and call multiple times
local start = tonumber(ARGV[1])
local count = 100
local results = {}
for i = start, start + count - 1 do
local val = redis.call('GET', 'item:' .. i)
if val then table.insert(results, val) end
end
return resultsDebugging Workflow
Step 1: Validate Syntax
redis-cli -p 6379 --eval your_script.lua key1 , arg1The --eval flag loads and executes, showing errors immediately.
Step 2: Use Redis.LOG for Debugging
Add logging inside your script:
local val = redis.call('GET', KEYS[1])
redis.log(redis.LOG_WARNING, 'Got value: ' .. tostring(val))
return valCheck Redis logs:
tail -f /var/log/redis/redis-server.logStep 3: Test with Minimal Reproduction
Create a test script that isolates the failing section:
redis-cli EVAL "local v = redis.call('TYPE', KEYS[1]); return v.ok" 1 testkeyBest Practices
- 1.Always use KEYS array for key access - Never construct key names from ARGV alone as this breaks cluster compatibility
- 2.Keep scripts short - Under 5000 instructions to avoid timeouts
- 3.Use script SHA caching - Load once, call many times:
```bash # Load script SHA=$(redis-cli SCRIPT LOAD "$(cat script.lua)")
# Call by SHA redis-cli EVALSHA $SHA 2 key1 key2 arg1 ```
- 1.Handle nil values explicitly - Lua nil propagates through operations unexpectedly
- 2.Use redis.pcall for non-critical operations - Returns error as table instead of throwing:
local result = redis.pcall('GET', KEYS[1])
if result.err then
-- handle error
end