{"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/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/multidb/failover.py#L48-L84","documentation":"Raised by `WeightBasedFailoverStrategy.database()` (redis/asyncio/multidb/failover.py:66) when iterating `self._databases` yields no database whose circuit breaker is in the CLOSED state. This is the inner strategy signal that failover cannot pick a target; the `DefaultFailoverStrategyExecutor` catches it and either retries or surfaces error 156.","triggerScenarios":"The failover strategy being asked (during command execution via `_check_active_database`, or during `set_active_database` fallback) to pick a healthy DB when every database's circuit is OPEN or HALF_OPEN.","commonSituations":"Total outage of all Redis endpoints; all circuits tripped by the failure detector; the grace period before HALF_OPEN has not elapsed; failure_rate_threshold/min_num_failures too aggressive.","solutions":["Restore at least one Redis endpoint so its circuit recovers to CLOSED (it will be probed in HALF_OPEN after the grace period).","Increase `grace_period` on the circuit breaker / DB config so circuits move to HALF_OPEN and get re-probed sooner if the backend recovered.","Tune failure-detector thresholds (`min_num_failures`, `failure_rate_threshold`, `failures_detection_window`) so transient errors do not open every circuit.","Add more databases (redundant regions) so a single-region outage cannot exhaust the list."],"exampleFix":"# before\n# all DBs unreachable -> every circuit OPEN -> NoValidDatabaseException on next command\n\n# after\n# 1. bring one endpoint back, then\n# 2. tune failover to recover faster\nfrom redis.multidb.circuit import DEFAULT_GRACE_PERIOD\ncfg = MultiDbConfig(\n    databases_config=[db_a, db_b],\n)\nfor db_cfg in cfg.databases_config:\n    db_cfg.grace_period = DEFAULT_GRACE_PERIOD  # shorten to retry HALF_OPEN sooner","handlingStrategy":"retry","validationCode":"from redis.multidb.circuit import State as CBState\n\ndef any_closed(client) -> bool:\n    return any(db.circuit.state == CBState.CLOSED for db, _ in client.get_databases())\n\n# do not trigger failover/command execution when any_closed(client) is False","typeGuard":"from redis.multidb.circuit import State as CBState\n\ndef has_closed_circuit(client) -> bool:\n    return any(db.circuit.state == CBState.CLOSED for db, _ in client.get_databases())","tryCatchPattern":"from redis.multidb.exception import NoValidDatabaseException\nimport asyncio\n\nfor _ in range(10):\n    try:\n        await client.set('k', 'v')\n        break\n    except NoValidDatabaseException:\n        await asyncio.sleep(1)\nelse:\n    raise","preventionTips":["Maintain redundant databases across regions/zones so a single outage cannot open every circuit.","Tune circuit-breaker `grace_period` and failure detector thresholds so circuits recover promptly.","Application-level circuit breaker: shed load when all DB circuits are OPEN instead of hammering the client."],"tags":["multidb","failover","circuit-breaker","outage"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}