redis/redis-py · error · ConnectionError

The previous master is now a slave

Error message

The previous master is now a slave

What it means

Raised as a ConnectionError inside SentinelManagedConnection.read_response (redis/asyncio/sentinel.py:94) when a ReadOnlyError is received on a connection pool flagged is_master. A ReadOnlyError from a node we believe is master means a failover has occurred: our cached master was demoted to a replica. The connection is forcibly disconnected so the next connect() re-queries Sentinel for the new master.

Source

Thrown at redis/asyncio/sentinel.py:94

        disconnect_on_error: Optional[float] = True,
        push_request: Optional[bool] = False,
    ):
        try:
            return await 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.
                await self.disconnect()
                raise ConnectionError("The previous master is now a slave")
            raise


class SentinelManagedSSLConnection(SentinelManagedConnection, SSLConnection):
    pass


class SentinelConnectionPool(ConnectionPool):
    """
    Sentinel backed connection pool.

    If ``check_connection`` flag is set to True, SentinelManagedConnection
    sends a PING command right after establishing the connection.
    """

    def __init__(self, service_name, sentinel_manager, **kwargs):
        kwargs["connection_class"] = kwargs.get(
            "connection_class",

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Retry the operation — the disconnect forces re-discovery of the new master on the next attempt.
  2. Wrap the call with a retry loop (or use the client retry_on_error with ReadOnlyError/ConnectionError) sized to the expected failover window.
  3. Ensure socket_timeout and retry config are tuned so rediscovery completes within your SLA.

Example fix

# before
await master.set('k', 'v')  # may raise ConnectionError mid-failover
# after
for _ in range(retries):
    try:
        return await master.set('k', 'v')
    except ConnectionError:
        await asyncio.sleep(backoff)
Defensive patterns

Strategy: retry

Try / catch

from redis.exceptions import ConnectionError, ReadOnlyError
for attempt in range(retries):
    try:
        return await master_client.execute_command(*args)
    except ConnectionError:
        await asyncio.sleep(backoff * (2 ** attempt))

Prevention

When it happens

Trigger: Issuing a write command through a master_for(...) client during/after a Sentinel failover; the pool still holds a connection to the demoted old master which now refuses writes with READONLY error.

Common situations: Active Sentinel failover in progress or just completed; brief window between master demotion and client rediscovery; repeated writes during topology churn.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/ebde22b441ce352a.json. Report an issue: GitHub.