How to fix ERR Write commands are not allowed from read-only scripts in Redis

RedisINTERMEDIATEMEDIUMUpdated Dec 22, 2025ยท 175 views

This error occurs when attempting write operations (SET, DEL, etc.) inside a Lua script executed with EVAL_RO or by a read-only user. Use EVAL for write operations or ensure your script only performs reads.

Quick fix

Fixes ERR Write commands are not allowed from read-only scripts

-- Example problematic script with EVAL_RO
EVAL_RO "redis.call('SET', KEYS[1], ARGV[1]); return redis.call('GET', KEYS[1])" 1 mykey myvalue
-- Error: ERR Write commands are not allowed from read-only scripts
  1. Identify the write commands in your script
  2. Use EVAL instead of EVAL_RO for write operations
  3. For read-only scripts, remove all write commands
  4. Connect to master instance for write operations
redis โ€” error
-- Example problematic script with EVAL_RO
EVAL_RO "redis.call('SET', KEYS[1], ARGV[1]); return redis.call('GET', KEYS[1])" 1 mykey myvalue
-- Error: ERR Write commands are not allowed from read-only scripts
ERR Write commands are not allowed from read-only scripts

What this error means

This error is raised by Redis when a Lua script attempts to execute write commands (such as SET, DEL, INCR, or even PUBLISH) while running in read-only mode. Redis enforces this restriction to maintain data consistency and proper replication behavior. Read-only scripts are executed using special command variants like EVAL_RO, EVALSHA_RO, or FCALL_RO. These variants are designed to route scripts to read replicas safely and can be killed without data loss using SCRIPT KILL. When Redis detects a write command within such a script, it immediately rejects the operation with this error. This behavior was introduced as part of Redis's scripting improvements in versions 7.0 and 7.2, where the distinction between read-only and write scripts became more strictly enforced. The restriction also applies when scripts are executed by users with read-only permissions or when connected to a read replica attempting write operations.

Typical symptoms

  • Script execution fails immediately when calling write commands like SET or DEL
  • Error appears when using EVAL_RO, EVALSHA_RO, or FCALL_RO command variants
  • Commands that worked with EVAL fail when switched to EVAL_RO
  • Replica connections reject scripts containing write operations
  • Scripts run by read-only users or accounts fail with this error

Common causes

  1. Using EVAL_RO instead of EVAL for scripts that perform write operations
  2. Lua script contains write commands like SET, DEL, INCR, LPUSH, or PUBLISH
  3. Script is executed by a user account with read-only permissions
  4. Connection is to a read replica rather than the master instance
  5. Script includes commands that Redis treats as writes (PUBLISH, SPUBLISH, PFCOUNT)
  6. Migrating code to Redis 7.0+ where read-only enforcement became stricter

How to fix "ERR Write commands are not allowed from read-only scripts"

1Identify the write commands in your script

First, review your Lua script to identify all commands that modify data:

lua
-- Example problematic script with EVAL_RO
EVAL_RO "redis.call('SET', KEYS[1], ARGV[1]); return redis.call('GET', KEYS[1])" 1 mykey myvalue
-- Error: ERR Write commands are not allowed from read-only scripts

Common write commands to look for:
- Data modification: SET, DEL, INCR, DECR, APPEND
- List operations: LPUSH, RPUSH, LPOP, RPOP
- Set operations: SADD, SREM
- Hash operations: HSET, HDEL
- Publishing: PUBLISH, SPUBLISH
- Special cases: PFCOUNT (treated as write in scripts)

2Use EVAL instead of EVAL_RO for write operations

If your script needs to perform write operations, use the standard EVAL command:

bash
# Wrong - using EVAL_RO with write commands
redis-cli EVAL_RO "redis.call('SET', 'counter', 1)" 0

# Correct - using EVAL for write operations
redis-cli EVAL "redis.call('SET', 'counter', 1)" 0

In application code:

javascript
// Node.js with ioredis
// Wrong
await redis.eval_ro("redis.call('INCR', KEYS[1])", 1, 'mycounter');

// Correct
await redis.eval("redis.call('INCR', KEYS[1])", 1, 'mycounter');
3For read-only scripts, remove all write commands

If you want to keep using EVAL_RO (for replica routing or killability), ensure your script only performs read operations:

lua
-- Read-only script - safe for EVAL_RO
local value = redis.call('GET', KEYS[1])
local count = redis.call('HLEN', KEYS[2])
return {value, count}
bash
redis-cli EVAL_RO "local value = redis.call('GET', KEYS[1]); return value" 1 mykey

You can also use the no-writes flag in Redis 7.0+ with shebang notation:

lua
#!lua flags=no-writes
return redis.call('GET', KEYS[1])
4Connect to master instance for write operations

If you're connected to a read replica, switch to the master instance:

javascript
// Node.js configuration
const Redis = require('ioredis');

// Wrong - trying writes on replica
const replica = new Redis({
  host: 'replica.redis.example.com',
  port: 6379
});

// Correct - use master for writes
const master = new Redis({
  host: 'master.redis.example.com',
  port: 6379
});

await master.eval("redis.call('SET', KEYS[1], ARGV[1])", 1, 'mykey', 'myvalue');

In Redis Cluster or Sentinel setups, ensure your client library routes write operations to the master automatically.

5Check user permissions for read-only accounts

If using ACLs, verify your user has write permissions:

bash
# Check current user permissions
ACL WHOAMI
ACL GETUSER myuser

# Create user with script write permissions
ACL SETUSER scriptuser on >password ~* +@all +eval

# Or specifically allow write commands needed
ACL SETUSER scriptuser on >password ~* +eval +set +del +incr

If the user is intentionally read-only, use a different user account for scripts requiring writes, or split your logic into separate read-only and write scripts.

Advanced notes

Redis 7.0+ Breaking Changes:
Starting with Redis 7.0, the enforcement of read-only scripts became stricter. Commands like PUBLISH and PFCOUNT are now treated as write commands even though they don't modify data directly. This change ensures consistent behavior across clustered environments and proper replication.

Performance Benefits of EVAL_RO:
Read-only scripts executed with EVAL_RO have several advantages:
- Can run on read replicas, distributing load away from the master
- Can be killed with SCRIPT KILL without data loss concerns
- Don't fail with OOM errors when Redis is near memory limits
- Not blocked during write pauses (coordinated failovers)

Function Libraries (Redis 7.0+):
Consider using Redis Functions instead of EVAL for complex logic:

bash
# Register a read-only function
FUNCTION LOAD "#!lua name=mylib
redis.register_function{function_name='myread', callback=function(keys, args) return redis.call('GET', keys[1]) end, flags={'no-writes'}}"

# Call it (automatically treated as read-only)
FCALL_RO myread 1 mykey

Cluster Considerations:
In Redis Cluster, read-only scripts can be routed to replicas using the READONLY command, improving read scalability. Write scripts must always execute on master nodes and respect key slot restrictions.

Frequently asked questions

What does "ERR Write commands are not allowed from read-only scripts" mean?
This error is raised by Redis when a Lua script attempts to execute write commands (such as SET, DEL, INCR, or even PUBLISH) while running in read-only mode. Redis enforces this restriction to maintain data consistency and proper replication behavior. Read-only scripts are executed using special command variants like EVAL_RO, EVALSHA_RO, or FCALL_RO.
How do I fix "ERR Write commands are not allowed from read-only scripts"?
This error occurs when attempting write operations (SET, DEL, etc.) inside a Lua script executed with EVAL_RO or by a read-only user. Use EVAL for write operations or ensure your script only performs reads.
What causes "ERR Write commands are not allowed from read-only scripts"?
Using EVAL_RO instead of EVAL for scripts that perform write operations Lua script contains write commands like SET, DEL, INCR, LPUSH, or PUBLISH Script is executed by a user account with read-only permissions
Is the "ERR Write commands are not allowed from read-only scripts" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading