redis/redis-py · critical · NoValidDatabaseException
No valid database available for communication
Error message
No valid database available for communication
What it means
Raised as NoValidDatabaseException by WeightBasedFailoverStrategy.database() (failover.py:61-66) when iterating the weighted database list yields no database with a CLOSED circuit — i.e. every database is currently OPEN/HALF_OPEN and unreachable. It is the strategy-level signal that there is no valid target to route to.
Solutions
- Catch NoValidDatabaseException at the command boundary and either degrade gracefully or surface a 503 to callers.
- Bring at least one endpoint back (network/Redis/lag) so its circuit returns to CLOSED via _check_db_health.
- Add more geographically diverse databases to databases_config so a single region outage cannot open every circuit.
- Tune circuit reset_timeout/grace_period and failover_delay so circuits recover and retry within your SLO.
- Verify there is no shared dependency (DNS, load balancer) whose failure opens all circuits at once.
Example fix
// before
resp = await client.execute_command("GET", "k") # propagates NoValidDatabaseException
// after
from redis.multidb.exception import NoValidDatabaseException, TemporaryUnavailableException
try:
resp = await client.execute_command("GET", "k")
except TemporaryUnavailableException:
resp = await fallback_store.get("k") # temporary - retry/degrade
except NoValidDatabaseException:
raise ServiceUnavailable("all Redis databases are down") Defensive patterns
Strategy: try-catch
Validate before calling
from redis.multidb.circuit import State as CBState
def has_closed_database(client) -> bool:
return any(d.circuit.state == CBState.CLOSED for d, _ in client.get_databases())
# before issuing a command when you suspect an outage:
if not has_closed_database(client):
raise ServiceUnavailable("all Redis circuits are open") Type guard
from redis.multidb.circuit import State as CBState
def any_circuit_closed(client) -> bool:
return any(d.circuit.state == CBState.CLOSED for d, _ in client.get_databases()) Try / catch
from redis.multidb.exception import NoValidDatabaseException
try:
resp = await client.execute_command("GET", "k")
except NoValidDatabaseException:
# no database can serve this request right now; degrade or fail hard
raise ServiceUnavailable("all Redis databases are unavailable") Prevention
- Distribute databases across independent regions/AZs to avoid correlated circuit opens.
- Tune circuit reset_timeout so circuits recover within your SLO.
- Monitor the count of CLOSED circuits; alert before it hits zero.
- Provide a cache/degraded fallback at the call site for NoValidDatabaseException.
- Keep auto_fallback_interval reasonable so the strategy re-tries promotion.
When it happens
Trigger: Triggered during command execution when DefaultCommandExecutor._check_active_database() invokes the failover strategy (because the active DB's circuit opened, or the auto_fallback_interval elapsed) and WeightBasedFailoverStrategy.database() finds all circuits non-CLOSED. Also reached directly if someone calls strategy.database().
Common situations: A region-wide outage taking down every Active-Active endpoint simultaneously; cascading circuit opens after a network partition; all databases in HALF_OPEN awaiting probe recovery; misconfigured weights with all endpoints down.
Related errors
- Cannot set active database, database is unhealthy
- Cannot set active database, database is unhealthy
- Initial connection failed - no active database found
- No database connections currently available. This is a…
- Initial health check failed. Initial health check policy
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/23da74206c09261f.
Report an issue: GitHub.
Appendix: source
Thrown at redis/asyncio/multidb/failover.py:66
async def execute(self) -> AsyncDatabase:
"""Execute the failover strategy."""
pass
class WeightBasedFailoverStrategy(AsyncFailoverStrategy):
"""
Failover strategy based on database weights.
"""
def __init__(self):
self._databases = WeightedList()
async def database(self) -> AsyncDatabase:
for database, _ in self._databases:
if database.circuit.state == CBState.CLOSED:
return database
raise NoValidDatabaseException("No valid database available for communication")
def set_databases(self, databases: Databases) -> None:
self._databases = databases
class DefaultFailoverStrategyExecutor(FailoverStrategyExecutor):
"""
Executes given failover strategy.
"""
def __init__(
self,
strategy: AsyncFailoverStrategy,
failover_attempts: int = DEFAULT_FAILOVER_ATTEMPTS,
failover_delay: float = DEFAULT_FAILOVER_DELAY,
):
self._strategy = strategy
self._failover_attempts = failover_attemptsView on GitHub (pinned to 6a6b581b48)