redis/redis-py · critical · NoValidDatabaseException

Initial connection failed - no active database found

Error message

Initial connection failed - no active database found

What it means

Raised by MultiDBClient.initialize() (as NoValidDatabaseException) after the initial health checks complete but no configured database ended up with a CLOSED circuit breaker, so there is nothing to promote to the active database that serves commands. It is the client's hard guarantee that an active, healthy database exists before any command is dispatched. In practice it signals total unavailability or a setup where every database's circuit was left OPEN at startup.

Solutions

  1. Verify each DatabaseConfig.from_url / host+port actually reaches a live Redis with redis-cli PING before constructing MultiDBClient.
  2. If some databases are optional at boot, set initial_health_check_policy=InitialHealthCheck.ONE_AVAILABLE so a single healthy DB is enough to initialize.
  3. Check network egress, DNS resolution, and TLS/credentials for every endpoint; fix the unreachable ones and restart the client.
  4. If using a custom CircuitBreaker, confirm it transitions to CBState.CLOSED when _check_db_health reports healthy (see _check_db_health in client.py:415).
  5. Wrap initialize() in try/except NoValidDatabaseException and surface a startup failure / retry with backoff rather than letting the app crash.

Example fix

// before
config = MultiDbConfig(databases_config=[
    DatabaseConfig(from_url="redis://wrong-host:6379"),
])
client = MultiDBClient(config)
await client.initialize()  # raises NoValidDatabaseException

// after
config = MultiDbConfig(
    databases_config=[
        DatabaseConfig(from_url="redis://redis-primary.local:6379", weight=10),
        DatabaseConfig(from_url="redis://redis-secondary.local:6379", weight=1),
    ],
    initial_health_check_policy=InitialHealthCheck.ONE_AVAILABLE,
)
client = MultiDBClient(config)
await client.initialize()
Defensive patterns

Strategy: try-catch

Validate before calling

from redis.asyncio import Redis
from redis.multidb.circuit import State as CBState

async def at_least_one_reachable(databases_config) -> bool:
    for cfg in databases_config:
        url = cfg.from_url
        try:
            r = Redis.from_url(url) if url else Redis(**cfg.client_kwargs)
            ok = await r.ping(); await r.aclose()
            if ok:
                return True
        except Exception:
            continue
    return False

# before constructing MultiDBClient:
if not await at_least_one_reachable(config.databases_config):
    raise RuntimeError("no reachable database; refusing to start")

Type guard

from redis.asyncio.multidb.config import DatabaseConfig

def has_valid_database_configs(cfgs: list) -> bool:
    return bool(cfgs) and all(
        isinstance(c, DatabaseConfig) and (c.from_url or c.from_pool or c.client_kwargs)
        for c in cfgs
    )

Try / catch

from redis.multidb.exception import NoValidDatabaseException

try:
    await client.initialize()
except NoValidDatabaseException:
    # startup cannot proceed; fail fast with actionable context
    logger.critical("MultiDBClient could not find a healthy active database at startup")
    raise

Prevention

When it happens

Trigger: Calling await client.initialize() (or letting execute_command/transaction/pubsub/pipeline trigger it lazily) when, after _perform_initial_health_check() returns, the loop at client.py:128-137 finds no database whose circuit.state == CBState.CLOSED. Happens when all databases fail their health checks but a lenient initial_health_check_policy gate was bypassed, when custom CircuitBreaker implementations never report CLOSED, or when the databases list resolves to entries that are all unreachable.

Common situations: All Redis endpoints misconfigured/unreachable at startup (wrong host/port, network partition, firewall), TLS/credentials mismatch so PING never succeeds, DNS not resolving any Enterprise endpoint, or supplying a databases_config whose URLs all point at downed instances. Also seen when a custom circuit breaker is injected that ignores the CLOSED transition driven by _check_db_health.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/0d1569121f845421. Report an issue: GitHub.

Appendix: source

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

                self._check_databases_health,
            )
        )

        is_active_db_found = False

        for database, weight in self._databases:
            # Set on state changed callback for each circuit.
            database.circuit.on_state_changed(self._on_circuit_state_change_callback)

            # Set states according to a weights and circuit state
            if database.circuit.state == CBState.CLOSED and not is_active_db_found:
                # Directly set the active database during initialization
                # without recording a geo failover metric
                self.command_executor._active_database = database
                is_active_db_found = True

        if not is_active_db_found:
            raise NoValidDatabaseException(
                "Initial connection failed - no active database found"
            )

        self.initialized = True

    def get_databases(self) -> Databases:
        """
        Returns a sorted (by weight) list of all databases.
        """
        return self._databases

    async def set_active_database(self, database: AsyncDatabase) -> None:
        """
        Promote one of the existing databases to become an active.
        """
        exists = None

        for existing_db, _ in self._databases:

View on GitHub (pinned to 6a6b581b48)