{"record":{"id":"23da74206c09261f","repo":"redis/redis-py","slug":"no-valid-database-available-for-communication","errorCode":null,"errorMessage":"No valid database available for communication","messagePattern":"No valid database available for communication","errorType":"exception","errorClass":"NoValidDatabaseException","httpStatus":null,"severity":"critical","filePath":"redis/asyncio/multidb/failover.py","lineNumber":66,"sourceCode":"    async def execute(self) -> AsyncDatabase:\n        \"\"\"Execute the failover strategy.\"\"\"\n        pass\n\n\nclass WeightBasedFailoverStrategy(AsyncFailoverStrategy):\n    \"\"\"\n    Failover strategy based on database weights.\n    \"\"\"\n\n    def __init__(self):\n        self._databases = WeightedList()\n\n    async def database(self) -> AsyncDatabase:\n        for database, _ in self._databases:\n            if database.circuit.state == CBState.CLOSED:\n                return database\n\n        raise NoValidDatabaseException(\"No valid database available for communication\")\n\n    def set_databases(self, databases: Databases) -> None:\n        self._databases = databases\n\n\nclass DefaultFailoverStrategyExecutor(FailoverStrategyExecutor):\n    \"\"\"\n    Executes given failover strategy.\n    \"\"\"\n\n    def __init__(\n        self,\n        strategy: AsyncFailoverStrategy,\n        failover_attempts: int = DEFAULT_FAILOVER_ATTEMPTS,\n        failover_delay: float = DEFAULT_FAILOVER_DELAY,\n    ):\n        self._strategy = strategy\n        self._failover_attempts = failover_attempts","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/multidb/failover.py#L48-L84","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\nresp = await client.execute_command(\"GET\", \"k\")  # propagates NoValidDatabaseException\n\n// after\nfrom redis.multidb.exception import NoValidDatabaseException, TemporaryUnavailableException\ntry:\n    resp = await client.execute_command(\"GET\", \"k\")\nexcept TemporaryUnavailableException:\n    resp = await fallback_store.get(\"k\")  # temporary - retry/degrade\nexcept NoValidDatabaseException:\n    raise ServiceUnavailable(\"all Redis databases are down\")","handlingStrategy":"try-catch","validationCode":"from redis.multidb.circuit import State as CBState\n\ndef has_closed_database(client) -> bool:\n    return any(d.circuit.state == CBState.CLOSED for d, _ in client.get_databases())\n\n# before issuing a command when you suspect an outage:\nif not has_closed_database(client):\n    raise ServiceUnavailable(\"all Redis circuits are open\")","typeGuard":"from redis.multidb.circuit import State as CBState\n\ndef any_circuit_closed(client) -> bool:\n    return any(d.circuit.state == CBState.CLOSED for d, _ in client.get_databases())","tryCatchPattern":"from redis.multidb.exception import NoValidDatabaseException\n\ntry:\n    resp = await client.execute_command(\"GET\", \"k\")\nexcept NoValidDatabaseException:\n    # no database can serve this request right now; degrade or fail hard\n    raise ServiceUnavailable(\"all Redis databases are unavailable\")","preventionTips":["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."],"tags":["multidb","failover","circuit-breaker","health-check","routing"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}