redis/redis-py · warning · ConnectionError
The previous master is now a slave
Error message
The previous master is now a slave
What it means
Raised by SentinelManagedConnection.read_response (redis/sentinel.py:91). When a Sentinel-managed connection used for the master role receives a ReadOnlyError from Redis, it means a failover occurred and the instance the client treated as master has been demoted to a replica (replicas reject writes with READONLY). The library calls self.disconnect() and raises redis.exceptions.ConnectionError so the next operation forces Sentinel to re-discover the new master.
Source
Thrown at redis/sentinel.py:91
disconnect_on_error: Optional[bool] = False,
push_request: Optional[bool] = False,
):
try:
return super().read_response(
disable_decoding=disable_decoding,
timeout=timeout,
disconnect_on_error=disconnect_on_error,
push_request=push_request,
)
except ReadOnlyError:
if self.connection_pool.is_master:
# When talking to a master, a ReadOnlyError when likely
# indicates that the previous master that we're still connected
# to has been demoted to a slave and there's a new master.
# calling disconnect will force the connection to re-query
# sentinel during the next connect() attempt.
self.disconnect()
raise ConnectionError("The previous master is now a slave")
raise
class SentinelManagedSSLConnection(SentinelManagedConnection, SSLConnection):
pass
class SentinelConnectionPoolProxy:
def __init__(
self,
connection_pool,
is_master,
check_connection,
service_name,
sentinel_manager,
):
self.connection_pool_ref = weakref.ref(connection_pool)
self.is_master = is_masterView on GitHub (pinned to da03cdc7e8)
Solutions
- Retry the operation: the next attempt triggers get_master_address() which re-queries Sentinel and connects to the new master.
- Wrap write operations in a retry/backoff loop catching redis.exceptions.ConnectionError.
- Ensure Sentinel quorum and sentinel_discover_* latency are healthy so re-discovery is fast.
- Tune connection_pool kwargs (e.g. lower health_check_interval, max_connections) so stale connections are reaped sooner.
Example fix
# before
client = sentinel.master_for('mymaster', socket_timeout=0.5)
client.set('k', 'v') # may raise mid-failover
# after
from redis.exceptions import ConnectionError
for attempt in range(5):
try:
client.set('k', 'v')
break
except ConnectionError:
time.sleep(0.1 * (2 ** attempt)) Defensive patterns
Strategy: retry
Try / catch
import time
from redis.exceptions import ConnectionError as RedisConnectionError
def write_with_failover_retry(client, *args, max_attempts=5, **kwargs):
last_err = None
for attempt in range(max_attempts):
try:
return client.set(*args, **kwargs)
except RedisConnectionError as e:
if 'previous master is now a slave' not in str(e).lower():
raise
last_err = e
time.sleep(0.1 * (2 ** attempt))
raise last_err Prevention
- Always wrap Sentinel master writes in retry logic with exponential backoff.
- Keep socket_timeout and health_check_interval low enough to detect failover quickly.
- Monitor Sentinel for failover events and alert on them.
- Avoid holding long-lived idle master connections across failovers.
When it happens
Trigger: A Sentinel-managed master connection (obtained via Sentinel.master_for(...)) performs a write/read-write command after a Sentinel failover has demoted the old master. The demoted node returns ReadOnlyError, which is caught in read_response; because connection_pool.is_master is True, the connection is torn down and this ConnectionError is raised.
Common situations: During or immediately after a Redis Sentinel failover; client holds stale pooled connections to the old master; failover completed but client's master_address cache has not yet been refreshed; network blip causing transient failover.
Related errors
- No slave found for {self.service_name!r}
- No master found for {service_name!r}{error_info}
- No database connections currently available. This is a tempo
- The previous master is now a slave
- No slave found for {self.service_name!r}
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/195a00d68a091eac.json.
Report an issue: GitHub.