This error occurs when your application attempts a write operation on a Redis replica instead of the primary master node. Redis replicas are read-only by design to maintain data consistency across the replication cluster.
In a Redis replication architecture, the primary master node handles all write operations while replica nodes synchronize data and serve read-only requests. When a write command (SET, DEL, INCR, HSET, etc.) is issued against a replica, Redis immediately rejects it with this READONLY error. This protection ensures that replicas remain exact copies of the master and prevents split-brain scenarios where replicas diverge from the master's state.
Connect to your Redis instance and query its replication status:
redis-cli -h <your-redis-host> -p <your-redis-port>
> INFO REPLICATIONLook for the role field in the output:
- If it shows role:master, your node is correct. The error comes from configuration issues.
- If it shows role:slave, you are connected to a replica and must switch to the master.
If you're on a replica, extract the master's address from the replication info:
redis-cli -h <replica-host> -p <replica-port>
> INFO REPLICATIONFind these fields in the output:
master_host:10.0.0.5
master_port:6379The master_host and master_port values are what your application should connect to.
Change your Redis client configuration to use the master node's address:
Node.js (ioredis):
const redis = require('ioredis');
const client = new redis({
host: 'master-host-ip', // NOT replica-host-ip
port: 6379,
password: 'your-password',
retryStrategy: (times) => Math.min(times * 50, 2000)
});Node.js (redis client):
const { createClient } = require('redis');
const client = createClient({
host: 'master-host-ip',
port: 6379
});Python (redis-py):
import redis
r = redis.Redis(
host='master-host-ip',
port=6379,
password='your-password',
decode_responses=True
)
# Test with a write
r.set('test-key', 'test-value')Java (Jedis):
Jedis jedis = new Jedis("master-host-ip", 6379);
jedis.set("test-key", "test-value");If you're using a cloud provider's managed Redis:
AWS ElastiCache:
1. Go to ElastiCache → Redis Clusters
2. Select your cluster
3. Under 'Endpoints', use the Primary Endpoint for writes
4. Do NOT use read replicas or reader endpoints
5. Example correct endpoint: my-redis.abc123.ng.0001.use1.cache.amazonaws.com:6379
Google Cloud Memorystore:
1. Open Memorystore for Redis
2. Click your instance
3. Use the Primary Endpoint in your application
4. Replicas are for failover, not client connections
Azure Cache for Redis:
1. Open your cache resource
2. Copy the Primary Connection String
3. Avoid geo-replicas for writes unless your app explicitly handles them
If running Redis with Sentinel for high availability, let your client discover the master automatically:
Node.js (ioredis with Sentinel):
const redis = require('ioredis');
const client = new redis.Sentinel([
{ host: 'sentinel-1', port: 26379 },
{ host: 'sentinel-2', port: 26379 },
{ host: 'sentinel-3', port: 26379 }
], {
name: 'mymaster' // Service name in sentinel config
});Python (redis-py with Sentinel):
from redis.sentinel import Sentinel
sentinels = [('sentinel-1', 26379), ('sentinel-2', 26379)]
sentinel = Sentinel(sentinels)
master = sentinel.master_for('mymaster', socket_timeout=0.1)
master.set('key', 'value')Sentinel automatically tracks master/replica topology and updates clients on failover.
If using Redis Cluster, ensure your client understands slot mapping:
Node.js (ioredis with Cluster):
const redis = require('ioredis');
const cluster = new redis.Cluster([
{ host: 'node-1', port: 6379 },
{ host: 'node-2', port: 6379 },
{ host: 'node-3', port: 6379 }
]);
cluster.set('key', 'value'); // Client routes to correct slot masterPython (redis-py with Cluster):
from rediscluster import RedisCluster
rc = RedisCluster(
startup_nodes=[{'host': 'node-1', 'port': 6379}],
decode_responses=True
)
rc.set('key', 'value')Cluster-aware clients automatically route write commands to the correct master node for each key's hash slot.
Replica Read-Only Configuration: The replica-read-only parameter in redis.conf defaults to yes. If set to no, replicas can accept writes, but this is strongly discouraged because it breaks replication consistency. The master's writes will overwrite replica-only changes, causing data loss.
Disabling Replica Read-Only (NOT RECOMMENDED): If you absolutely must write to a replica:
redis-cli -h <replica-host>
> CONFIG SET replica-read-only noBut understand: writes to a replica are lost on failover, and the master's replication stream will overwrite them anyway.
DNS and Client-Side Caching: Some Redis clients cache DNS results or connection objects. After failover or topology changes, stale caches may point to old nodes. Solutions: configure short DNS TTLs, use client libraries with auto-reconnection, or implement application-level health checks.
Failover Scenarios: In production, failover events (master down, Sentinel promoting replica) cause temporary unavailability. Client libraries should:
1. Detect the READONLY error
2. Refresh their node list
3. Retry on the newly promoted master
IORedis and redis-py Sentinel clients handle this automatically.
Cluster vs Replication: Don't confuse Redis Cluster (horizontal scaling) with Redis Replication (HA). In Cluster mode, each slot has a master and replicas; writes must go to the slot's master. In Replication mode, there's one master and N replicas. Both reject writes on replicas by default.
ERR fsync error
How to fix "ERR fsync error" in Redis
CLUSTERDOWN The cluster is down
How to fix 'CLUSTERDOWN The cluster is down' in Redis
ERR Job for redis-server.service failed because a timeout was exceeded
Job for redis-server.service failed because a timeout was exceeded
ERR Unbalanced XREAD list of streams
How to fix "ERR Unbalanced XREAD list" in Redis
ERR syntax error
How to fix "ERR syntax error" in Redis