Redis Lua Script Time Limit Exceeded: Root Causes and Minimal Fixes

Topic: redis-lua-script-time-limit-exceededUpdated 8/1/2026

Quick Answer

  • What it is: Redis's lua-time-limit (default 5000ms) is a soft threshold—after it's exceeded, Redis marks the script as slow and starts accepting SCRIPT KILL, but the script keeps running until it finishes or is killed.
  • First check: Run redis-cli SLOWLOG GET to identify slow scripts, and redis-cli CONFIG GET lua-time-limit to confirm your current threshold.
  • Minimal fix: Kill a stuck script with redis-cli SCRIPT KILL (safe if no writes occurred); if writes already happened, you must use SHUTDOWN NOSAVE (data loss risk).
  • Prevention: Keep scripts short, avoid KEYS and unbounded loops, use redis.pcall for error handling, and test with redis-cli --eval before production.
  • Version boundary: This behavior applies to Redis 2.6+ where Lua scripting was introduced; the default 5000ms limit has remained consistent across all subsequent versions.

What Problem It Solves

Redis executes Lua scripts atomically, blocking all other commands while a script runs. Without a safety mechanism, an infinite loop or poorly optimized script would freeze your Redis instance indefinitely, taking down every application that depends on it.

The lua-time-limit configuration provides a soft interruption point: once a script runs longer than the threshold, Redis starts accepting SCRIPT KILL commands. This gives operators a way to recover from runaway scripts without requiring a full instance restart.

Note that this is not a hard timeout. Redis cannot forcibly stop a running Lua script mid-execution because that would risk leaving the dataset in an inconsistent state. Instead, it flips a flag that allows an external SCRIPT KILL to interrupt the script at the next safe opportunity.

When This Error or Setup Appears

You'll encounter the BUSY Redis is busy running a script error in these scenarios:

  • Infinite loops: A script contains while true do ... end or a recursive pattern that never terminates.
  • Heavy computation: Scripts performing complex calculations, large table iterations, or expensive string operations exceed the 5-second default.
  • Blocking commands inside scripts: Using commands like BLPOP or WAIT inside a Lua script can block indefinitely.
  • Large data processing: Scripts that iterate over many keys or process large sorted sets, hashes, or lists.
  • Concurrent access: Multiple clients attempt to execute commands while a long-running script holds the instance.

The error typically appears in application logs as:

BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

Minimal Working Configuration

The default configuration is usually sufficient for most workloads. To explicitly set the limit:

BASH
# Check current value
redis-cli CONFIG GET lua-time-limit

# Set to 5 seconds (default)
redis-cli CONFIG SET lua-time-limit 5000

# Set to 10 seconds
redis-cli CONFIG SET lua-time-limit 10000

# Disable the limit (NOT recommended for production)
redis-cli CONFIG SET lua-time-limit 0

To make the change permanent, add to your redis.conf:

CONF
# redis.conf
lua-time-limit 5000

For a minimal test script that demonstrates the timeout behavior:

LUA
-- slow_script.lua
local i = 0
while true do
  i = i + 1
  if i % 1000000 == 0 then
    redis.call('SET', 'progress', i)
  end
end

Load and run it:

BASH
redis-cli --eval slow_script.lua

After 5 seconds, from another terminal:

BASH
redis-cli SCRIPT KILL

Parameters and Environment Variables

ParameterDefaultDescription
lua-time-limit5000Threshold in milliseconds after which Redis marks a script as slow and accepts SCRIPT KILL. Set to 0 to disable the limit entirely.

Key behaviors to understand:

  • Soft limit: The script continues running after the threshold; Redis only starts accepting kill commands.
  • Write detection: If the script has performed any write operation (e.g., SET, DEL, LPUSH), SCRIPT KILL will fail. Redis cannot safely roll back writes, so you must use SHUTDOWN NOSAVE.
  • No environment variables: This is a runtime configuration, not an environment variable. Set it via CONFIG SET, redis.conf, or the command-line flag --lua-time-limit.

Root Cause Analysis

The root cause of BUSY Redis is busy running a script is always the same: a Lua script is executing for longer than lua-time-limit milliseconds, and Redis has flagged it as slow.

The underlying mechanics:

  1. Atomic execution: Redis runs Lua scripts in a single-threaded event loop. While a script executes, no other commands are processed.
  2. Soft timeout: When the script exceeds lua-time-limit, Redis sets a flag that allows SCRIPT KILL to interrupt the script at the next Lua VM instruction boundary.
  3. Write safety: If the script has already modified data, Redis refuses SCRIPT KILL because terminating mid-script could leave partial writes. The only recovery is SHUTDOWN NOSAVE, which discards all unsaved data.

Common root causes in production:

  • Unbounded loops: Scripts that iterate until a condition is met without a maximum iteration count.
  • KEYS command: Using redis.call('KEYS', '*') inside a script forces a full keyspace scan, which is O(N) and blocks the instance.
  • Large collection operations: Iterating over a sorted set with millions of members using ZRANGE or ZSCAN inside a loop.
  • String concatenation in loops: Building large strings with .. in a loop creates O(N²) memory copies.
  • External calls: Scripts that attempt to perform network I/O or file operations (which Lua supports but Redis discourages).

Common Errors and Fixes

ErrorCauseFix
BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.Script exceeded lua-time-limitRun SCRIPT KILL if no writes occurred; otherwise SHUTDOWN NOSAVE (data loss risk)
ERR Error running script (call to f_...): @user_script:1: Script attempted to access nonexistent global variable '...'Script references an undefined global variableDefine all variables inside the script or pass them as arguments via KEYS and ARGV
ERR Error running script (call to f_...): @user_script:1: Wrong number of args calling Redis command From Lua scriptIncorrect argument count in redis.call or redis.pcallVerify the command signature; e.g., SET requires key and value, GET requires one key
ERR Error running script (call to f_...): @user_script:1: Lua redis() command arguments must be strings or integersNon-string/integer type passed to redis.callConvert values with tostring() or tonumber() before passing; never pass Lua tables directly

Recovery Procedure

BASH
# Step 1: Check if the script has written data
redis-cli INFO keyspace

# Step 2: Try to kill the script (works only if no writes occurred)
redis-cli SCRIPT KILL

# Step 3: If SCRIPT KILL fails, you must restart with data loss
redis-cli SHUTDOWN NOSAVE
redis-server /path/to/redis.conf

Production Notes and Security Checks

Monitoring and Alerting

  • Enable slowlog: redis-cli CONFIG SET slowlog-log-slower-than 1000 to capture scripts taking over 1 second.
  • Monitor busy state: Alert on the BUSY error appearing in application logs.
  • Track script execution time: Use redis-cli --latency and redis-cli --stat to observe instance responsiveness.

Prevention Checklist

  1. Keep scripts short: Aim for sub-millisecond execution. If a script takes longer, move the logic to the application layer.
  2. Avoid KEYS: Use SCAN in the application, or pass specific keys via KEYS array.
  3. Use redis.pcall: Wrap commands in redis.pcall to handle errors gracefully instead of aborting the entire script.
  4. Test before deploy: Run redis-cli --eval script.lua key1 key2 , arg1 arg2 in a staging environment.
  5. Set a sane lua-time-limit: Keep the default 5000ms unless you have a specific reason to change it. Setting it to 0 disables the safety mechanism entirely.
  6. Restrict network access: Bind Redis to trusted interfaces only (bind 127.0.0.1 or internal IPs) and require authentication (requirepass).
  7. Regular script review: Audit scripts for unbounded loops, large data operations, and blocking commands.

Security Considerations

  • Never set lua-time-limit to 0 in production: A single infinite loop will permanently block the instance, requiring a restart with data loss.
  • Limit script complexity: Redis Lua scripts run with full access to the Redis API. A compromised application that can execute arbitrary Lua can read or modify any key.
  • Use EVAL with care: Only pass trusted scripts to EVAL or EVALSHA. Never accept script source from user input.

FAQ

Q: What happens if I set lua-time-limit to 0?

A: Setting it to 0 disables the timeout mechanism entirely. Redis will never mark a script as slow and will never accept SCRIPT KILL. An infinite loop will block the instance permanently, and the only recovery is SHUTDOWN NOSAVE followed by a restart. This is not recommended for any environment, especially production.

Q: How do I safely terminate a long-running Lua script without stopping the service?

A: First, try redis-cli SCRIPT KILL. This works only if the script has not performed any write operations. If the script has already written data, SCRIPT KILL will fail with a UNKILLABLE error. In that case, you must use SHUTDOWN NOSAVE, which restarts Redis and discards all unsaved data. To minimize data loss, ensure you have a persistence strategy (RDB snapshots or AOF) with frequent saves.

Q: How can I prevent Lua script timeouts in the first place?

A: Follow these practices: keep scripts minimal and focused on atomic operations; avoid loops that iterate over large collections; use redis.pcall to handle errors gracefully; test scripts with redis-cli --eval and measure execution time; monitor with SLOWLOG to catch slow scripts early; and consider breaking large operations into multiple smaller scripts or using Redis transactions (MULTI/EXEC) instead.

Related Guides