What it means
The script you sent with EVAL or EVALSHA (or a function called with FCALL) stopped with a Lua
error. Redis reports it as an ERR, starting with where it happened and ending with which script:
(error) ERR user_script:1: Script attempted to access nonexistent global variable 'foo' script: 47e30a5d10d5694f99208f9adc4fe3bf2450edbd, on @user_script:1.
user_script:1:is the line of the script (functions sayuser_function:<line>:).script: 47e3…is the script’s SHA1, the oneEVALSHAuses; for a function it’s the function’s name.
A script isn’t a transaction that rolls back: anything it wrote before the failing line stays
written. Redis 6.2 and older word runtime errors differently,
ERR Error running script (call to f_<sha1>): ….
The messages you’ll meet most:
| Message | What happened |
|---|---|
Script attempted to access nonexistent global variable 'x' | The script read a name that was never declared: a typo, or a variable defined nowhere |
Attempt to modify a readonly table | The script assigned a global (x = 1); scripts must use local |
attempt to perform arithmetic on local 'n' (a boolean value) | n came from redis.call('GET', …) on a missing key, which gives false, not nil |
attempt to perform arithmetic on field '?' (a string value) | Arithmetic on text that isn’t a number, such as an ARGV value of abc |
attempt to index local 't' (a nil value) | t.name where t is nil |
attempt to concatenate a nil value | .. with a nil, often a missing ARGV[n] |
Error compiling script (new function): user_script:1: … | A Lua syntax error; nothing ran |
Lua redis lib command arguments must be strings or integers | redis.call was given a table, a boolean or a nil (such as KEYS[1] when no key was passed) |
Please specify at least one argument for this redis lib call | redis.call() with nothing in it |
Errors from the commands a script runs keep their own code and wording, with the script’s SHA1 and
line added: a GET on a hash inside a script is still
WRONGTYPE, and INCR on a word is still
not an integer.
Common causes
- A missing
local. Redis protects the global table: reading an undeclared name fails with “nonexistent global variable”, and writing one fails with “readonly table”. - Treating a missing key as
nil.redis.call('GET', KEYS[1])returnsfalsefor a missing key (a Redis null becomes Lua’sfalse), soif v == nilnever fires andv + 1fails. - Strings used as numbers. Everything in
KEYSandARGV, and every reply fromGET, is a string. Lua converts digits automatically in arithmetic, but not text, and comparing a string with a number fails (attempt to compare number with string). - A missing argument: the script reads
ARGV[2]but the caller sent one, so it’snil. - A Lua syntax error, often from building the script by string concatenation in your code.
How to fix it
Declare every variable with local
local count = tonumber(redis.call('GET', KEYS[1]) or '0')
return count + tonumber(ARGV[1])
Handle missing keys as false
local v = redis.call('GET', KEYS[1])
if not v then
return nil -- or a default
end
Convert and check arguments
Use tonumber(ARGV[1]), and check the result: tonumber('abc') is nil, so
local n = tonumber(ARGV[1]); if not n then return redis.error_reply('ERR amount must be a number') end
turns a confusing Lua error into a clear one ((error) ERR amount must be a number when called
with abc). Pass every key in KEYS and every value in ARGV; never build key names inside the
script.
Find the line
The number after user_script: is the line. For anything longer than a few lines, Redis’s Lua
debugger steps through a script without touching your data (by default it runs in a forked copy
that’s thrown away):
redis-cli --ldb --eval ./myscript.lua key1 key2 , arg1 arg2
Use it on a development server: in the default mode it forks the server for each session.
Catch errors you expect
redis.pcall returns a command’s error to the script as a table instead of stopping it, so the
script can decide what to do. redis.error_reply('MYERR …') returns your own error, code
included.
Reproduce it
Each script below ran with EVAL … 0 (or with the keys shown) on Redis 8.10.2 and Valkey 8.1.10, in
database 15; both gave the same messages and SHA1s unless noted. A global:
EVAL "return foo" 0
(error) ERR user_script:1: Script attempted to access nonexistent global variable 'foo' script: 47e30a5d10d5694f99208f9adc4fe3bf2450edbd, on @user_script:1.
EVAL "x = 1" 0
(error) ERR user_script:1: Attempt to modify a readonly table script: 34bce5f775de97f557a34088509c8bfe1ea17e52, on @user_script:1.
A missing key read with GET, then used as a number:
EVAL "local n = redis.call('GET', KEYS[1]) return n + 1" 1 seo:err:missing
(error) ERR user_script:1: attempt to perform arithmetic on local 'n' (a boolean value) script: 8a50c244a122e67dcec077dc0aa03549f5b8bd5f, on @user_script:1.
A write before the error isn’t undone:
EVAL "redis.call('SET', KEYS[1], 'written') return foo" 1 seo:err:w
(error) ERR user_script:1: Script attempted to access nonexistent global variable 'foo' script: 8c84fa2df321967d3d42a3db41754a94f80359fb, on @user_script:1.
GET seo:err:w
"written"
Compile errors (nothing ran):
EVAL "return (" 0
(error) ERR Error compiling script (new function): user_script:1: unexpected symbol near '<eof>'
EVAL "retrun 1" 0
(error) ERR Error compiling script (new function): user_script:1: '=' expected near '1'
Arguments and values:
EVAL "return ARGV[1] + 1" 0 abc
(error) ERR user_script:1: attempt to perform arithmetic on field '?' (a string value) script: 84834ee62a493ae7cb74fa5841a3a02222e583ba, on @user_script:1.
EVAL "return 'a' .. nil" 0
(error) ERR user_script:1: attempt to concatenate a nil value script: a954e0524e4e64a9f26274ffcefea47159ca7c15, on @user_script:1.
EVAL "local t = nil return t.name" 0
(error) ERR user_script:1: attempt to index local 't' (a nil value) script: 0fda25fc4233a6d8067d369b564c60e890b6ad3e, on @user_script:1.
EVAL "return redis.call('SET', KEYS[1], nil)" 1 seo:err:k
(error) ERR Lua redis lib command arguments must be strings or integers script: ccc37fbed5e464fd504faa7028bb4676f0663acb, on @user_script:1.
Valkey words the last one ERR Command arguments must be strings or integers, and redis.call()
with nothing in it ERR Please specify at least one argument for this call (Redis: … for this redis lib call). error('boom') gave ERR user_script:1: boom script: …, and
return redis.error_reply('MYERR something went wrong') gave (error) MYERR something went wrong,
with its own code and no suffix. redis.pcall('INCR', KEYS[1]) on a key holding hello returned
(error) ERR value is not an integer or out of range without the script suffix.
A function on a temporary Redis 8.10.2 server, FCALL broken 0, where broken returned foo:
ERR user_function:2: Script attempted to access nonexistent global variable 'foo' script: broken, on @user_function:2.
In Inlet
Query tabs take redis-cli commands, so you can run EVAL with the script in quotes and see the
reply as a table, or the error. Stop kills a script that runs too long, as long as it hasn’t
written anything yet (see BUSY). When a script fails, Inlet shows the
server’s message and links to this page.