Redis Error Guide: 'BUSY Redis is busy running a script' — Blocked by a Long Lua Script
Fix 'BUSY Redis is busy running a script' errors: lua-time-limit, SCRIPT KILL vs SHUTDOWN NOSAVE, looping Lua, and writing non-blocking scripts.
- #redis
- #troubleshooting
- #errors
- #lua
Overview
BUSY Redis is busy running a script is returned to other clients while a Lua script (or FUNCTION) has been running longer than lua-time-limit (default 5000 ms). Redis is single-threaded for command execution, so a script that runs long blocks every other command. Once the script passes lua-time-limit, Redis starts replying BUSY to incoming commands so operators can intervene — but the script itself keeps running until it finishes or is killed.
The literal error:
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
Crucially, SCRIPT KILL only works if the script has not yet performed a write. If it has already written data, killing it would leave the dataset half-modified, so Redis refuses SCRIPT KILL and your only option is SHUTDOWN NOSAVE (which drops the un-persisted writes). This makes long write-heavy scripts genuinely dangerous.
Symptoms
- Most clients suddenly get
BUSY Redis is busy running a scriptfor several seconds or longer. INFO stats/ latency dashboards show a hard stall;instantaneous_ops_per_secdrops to near zero.- One
EVAL/EVALSHA/FCALLcall is stuck; everything else queues behind it. - After the incident,
SLOWLOGshows a very slowEVAL.
redis-cli PING
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
Common Root Causes
1. A script iterating over a huge dataset
Lua that loops over KEYS, a big redis.call('KEYS', ...), or a large set with per-element work easily exceeds 5 s.
2. An infinite or unbounded loop in Lua
A while with a bad exit condition, or recursion, never returns — permanently BUSY until killed.
3. lua-time-limit set too low (or expectations too high)
lua-time-limit is a warning threshold, not a kill switch. A legitimately heavy but bounded script trips BUSY even though it will finish.
redis-cli CONFIG GET lua-time-limit
# redis.conf
lua-time-limit 5000
4. Blocking work inside a script
Scripts must be fast and deterministic. Heavy cjson decoding of large blobs, big SORT, or ZRANGEBYSCORE over millions of members inside one EVAL stalls the server.
Diagnostic Workflow
Step 1: Confirm a script is the blocker
redis-cli INFO stats | grep instantaneous_ops_per_sec
redis-cli --latency-history -i 1 # from a separate connection
If ops drop to ~0 and you get BUSY on PING, a script is running.
Step 2: Decide if it is safe to kill
# If the script has NOT written yet, this works:
redis-cli SCRIPT KILL
OK
If it returns UNKILLABLE Sorry the script already executed write commands against the dataset, the script has written and cannot be safely killed.
Step 3: Last resort for a write-heavy stuck script
# Drops writes made since the last save — DATA LOSS of un-persisted changes
redis-cli SHUTDOWN NOSAVE
Only do this knowingly; on a replica-backed setup, failover first if possible.
Step 4: After recovery, find the offending script
redis-cli SLOWLOG GET 10
redis-cli SCRIPT EXISTS <sha> # confirm which cached script ran
1) 1) (integer) 214
2) (integer) 1719950400
3) (integer) 8123456 # microseconds ~ 8.1s
4) 1) "EVALSHA"
2) "a1b2c3..."
Example Root Cause Analysis
A rate-limiter deploy shipped an EVAL that, under high cardinality, iterated every member of a large sorted set to expire stale entries. Under load one call ran ~30 s and clients flooded with BUSY.
redis-cli SCRIPT KILL
(error) UNKILLABLE Sorry the script already executed write commands against the dataset.
Because the script had already called ZREM, SCRIPT KILL was refused. The node had a replica, so ops promoted the replica and SHUTDOWN NOSAVE the wedged primary rather than lose the whole cluster’s availability.
The real fix was rewriting the script to be bounded — process at most N elements per call using ZRANGEBYSCORE ... LIMIT 0 100 and let repeated invocations catch up, instead of scanning the entire set in one pass:
-- bounded: never touches more than 100 members per EVAL
local stale = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1], 'LIMIT', 0, 100)
After the change, no single EVAL exceeded a few milliseconds and BUSY never recurred.
Prevention Best Practices
- Keep Lua scripts short, bounded, and deterministic — process a fixed batch per call, never the whole dataset.
- Never do unbounded loops or large
KEYS/SORT/cjsonwork inside a script; page the work across invocations. - Treat
lua-time-limitas a warning, not a safety net — scripts should finish in single-digit milliseconds. - Run write-heavy maintenance from a client with
SCAN, not a single monsterEVAL. - Always have a replica so you can fail over instead of
SHUTDOWN NOSAVEon a wedged primary. - Review
SLOWLOGfor slowEVAL/EVALSHAafter any script deploy; see more Redis error guides.
Quick Command Reference
# Confirm a script is blocking
redis-cli INFO stats | grep instantaneous_ops_per_sec
redis-cli --latency-history -i 1
# Kill it (only if no writes yet)
redis-cli SCRIPT KILL
# Last resort for a write-heavy stuck script (data loss)
redis-cli SHUTDOWN NOSAVE
# Post-incident forensics
redis-cli CONFIG GET lua-time-limit
redis-cli SLOWLOG GET 10
Conclusion
BUSY Redis is busy running a script means a Lua script has exceeded lua-time-limit and is blocking the single command thread. Your options are narrow by design:
- If the script has not written,
SCRIPT KILLstops it cleanly. - If it has already written, only
SHUTDOWN NOSAVEclears it — at the cost of un-persisted data. - Root causes are almost always unbounded loops or scripts that iterate huge datasets in one call.
The durable fix is writing scripts that touch a bounded batch per invocation and finish in milliseconds. Keep a replica so you can fail over instead of hard-killing a primary, and watch SLOWLOG after every script change.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.