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
- Verify each DatabaseConfig.from_url / host+port actually reaches a live Redis with redis-cli PING before constructing MultiDBClient.
- If some databases are optional at boot, set initial_health_check_policy=InitialHealthCheck.ONE_AVAILABLE so a single healthy DB is enough to initialize.
- Check network egress, DNS resolution, and TLS/credentials for every endpoint; fix the unreachable ones and restart the client.
- 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).
- 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
- Pre-flight PING every DatabaseConfig endpoint before constructing MultiDBClient.
- Use InitialHealthCheck.ONE_AVAILABLE when not all regions must be up at boot.
- Never deploy with a single-database databases_config if availability matters.
- Alert on NoValidDatabaseException at startup separately from runtime command errors.
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
- Initial health check failed. Initial health check policy
- Cannot set active database, database is unhealthy
- Cannot set active database, database is unhealthy
- No valid database available for communication
- Unhealthy database
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)