redis/redis-py · error · NoValidDatabaseException

Cannot set active database, database is unhealthy

Error message

Cannot set active database, database is unhealthy

What it means

Raised by `MultiDBClient.set_active_database()` (redis/asyncio/multidb/client.py:175) when the target database is a registered member but its circuit breaker is not in the CLOSED state after a fresh `_check_db_health()` call. Promoting an OPEN/HALF_OPEN database as active would route commands to a known-unhealthy endpoint, so the client refuses with NoValidDatabaseException.

Source

Thrown at redis/asyncio/multidb/client.py:175

        for existing_db, _ in self._databases:
            if existing_db == database:
                exists = True
                break

        if not exists:
            raise ValueError("Given database is not a member of database list")

        await self._check_db_health(database)

        if database.circuit.state == CBState.CLOSED:
            highest_weighted_db, _ = self._databases.get_top_n(1)[0]
            await self.command_executor.set_active_database(
                database, GeoFailoverReason.MANUAL
            )
            return

        raise NoValidDatabaseException(
            "Cannot set active database, database is unhealthy"
        )

    async def add_database(
        self, config: DatabaseConfig, skip_initial_health_check: bool = True
    ):
        """
        Adds a new database to the database list.

        Args:
            config: DatabaseConfig object that contains the database configuration.
            skip_initial_health_check: If True, adds the database even if it is unhealthy.
        """
        # The retry object is not used in the lower level clients, so we can safely remove it.
        # We rely on command_retry in terms of global retries.
        config.client_kwargs.update({"retry": Retry(retries=0, backoff=NoBackoff())})

        if config.from_url:

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Wait for the target database's circuit to recover to CLOSED (monitor via health checks) and retry `set_active_database`.
  2. Pick a different database from `get_databases()` whose circuit is CLOSED.
  3. Tune the circuit breaker (`grace_period`) and health-check probes so transient slowness does not trip the circuit.
  4. Verify the underlying Redis endpoint is actually reachable and responsive before attempting the switch.

Example fix

# before
await client.set_active_database(target_db)  # NoValidDatabaseException

# after
from redis.multidb.circuit import State as CBState
healthy = [db for db, _ in client.get_databases() if db.circuit.state == CBState.CLOSED]
if healthy:
    await client.set_active_database(healthy[0])
else:
    # wait for recovery or alert
    raise RuntimeError('no healthy database to promote')
Defensive patterns

Strategy: validation

Validate before calling

from redis.multidb.circuit import State as CBState

async def can_promote(client, database) -> bool:
    await client._check_db_health(database)  # refresh circuit state
    return database.circuit.state == CBState.CLOSED

# gate set_active_database on this returning True

Type guard

from redis.multidb.circuit import State as CBState

def circuit_is_closed(database) -> bool:
    return database.circuit.state == CBState.CLOSED

Try / catch

from redis.multidb.exception import NoValidDatabaseException

try:
    await client.set_active_database(db)
except NoValidDatabaseException as e:
    if 'unhealthy' in str(e):
        # wait for recovery, or pick another CLOSED db
        ...
    raise

Prevention

When it happens

Trigger: Calling `await client.set_active_database(db)` for a database whose latest health check failed (circuit OPEN), or which is in HALF_OPEN awaiting probe recovery. The health check at client.py:166 runs immediately before the circuit-state check at client.py:168.

Common situations: Manually failing over to a database that is still down; race where the DB goes unhealthy between the membership check and the health check; aggressive circuit-breaker thresholds marking a slow DB as OPEN.

Related errors


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